Part 129: Enhancing User Authentication in Your Web Application
[App] Authentication User Database

In our previous exploration, we established a "Sign Up" page for new users to register on our website. This involved creating a form that collects user data and integrates it with our backend database using Prisma. Today, we'll focus on refining the "Sign In" functionality to ensure it operates seamlessly with our existing database, enabling users to log in with their registered credentials.
Understanding the Current Setup
Before diving into the new implementation, let's review our current setup. We have a "Sign In" page with a form where users can enter their email and password. The form submission triggers the signInAction, which we need to update to authenticate users against our database rather than using placeholder logic.
The Problem with Current Sign In
The existing signInAction uses a temporary function, authenticate, which allows any user with "test" as the password to log in. This is inadequate for real-world applications, where credentials should be verified against stored data.
Updating the Sign In Logic
Creating the authenticateUser Function
authenticateUser FunctionFirst, we'll create a new function, authenticateUser, in the lib/users.js module. This function will query the database to find a user with the provided email and password.
// File path: lib/users.js
import { db } from './db';
export async function authenticateUser(email, password) {
return await db.user.findUnique({
where: { email, password },
});
}
export async function createUser({ email, name, password }) {
return await db.user.create({
data: { email, name, password },
});
}The authenticateUser function performs a database query to find a unique user matching the given email and password. If found, it returns the user object; otherwise, it returns null, indicating invalid credentials.
Modifying the signInAction
signInActionNext, we'll update the signInAction to use the authenticateUser function, replacing the placeholder logic.
// File path: app/sign-in/actions.js
'use server';
import { redirect } from 'next/navigation';
import { setSessionCookie } from '@/lib/auth';
import { authenticateUser } from '@/lib/users';
export async function signInAction(formData) {
console.log('[signInAction]', formData);
const email = formData.get('email');
const password = formData.get('password');
const user = await authenticateUser(email, password);
if (!user) {
return { isError: true, message: 'Invalid credentials' };
}
await setSessionCookie(user);
redirect('/');
}This updated action uses authenticateUser to verify credentials and, upon success, sets a session cookie and redirects the user to the home page. If authentication fails, it returns an error message.
Enhancing User Experience
Handling Duplicate Registrations
In the signUpAction, we encountered an issue with duplicate email registrations. To address this, we need to handle the unique constraint error gracefully by notifying the user.
// File path: app/sign-up/actions.js
export async function signUpAction(formData) {
const data = {
email: formData.get('email'),
name: formData.get('name'),
password: formData.get('password'),
};
// TODO validate data / handle duplicate email
try {
const user = await createUser(data);
console.log('[signUpAction] user:', user);
await setSessionCookie(user);
redirect('/');
} catch (error) {
if (error.code === 'P2002') { // Prisma unique constraint error
return { isError: true, message: 'Email already exists' };
}
throw error;
}
}Displaying Session Data
For better debugging and to enhance development visibility, we can log user session data in the NavBar component.
// File path: components/NavBar.jsx
import { getUserFromSession } from '@/lib/auth';
import NavLink from './NavLink';
import SignOutButton from './SignOutButton';
export default async function NavBar() {
const user = await getUserFromSession();
console.log('[NavBar] user:', user);
return (
<nav>
<ul className="flex gap-2">
<li className="font-bold font-orbitron">
<NavLink href="/">
Indie Gamer
</NavLink>
</li>
<li className="ml-auto">
<NavLink href="/reviews">
Reviews
</NavLink>
</li>
<li>
<NavLink href="/about" prefetch={false}>
About
</NavLink>
</li>
{user ? (
<li>
<SignOutButton />
</li>
) : (
<li>
<NavLink href="/sign-in">
Sign in
</NavLink>
</li>
)}
</ul>
</nav>
);
}Conclusion
By updating the "Sign In" logic to authenticate users against our database, we've strengthened our application's security and usability. We've also improved error handling for duplicate registrations and enhanced session visibility. These changes contribute to a more robust and user-friendly authentication system. In future posts, we'll explore further enhancements, such as securing passwords and implementing email verification. Stay tuned for more!
Last updated