- Published on
Auth.js: Authentication in Next.js
- Authors
- Name
- Shelton Ma
Configuration in Auth.js
Setting Up
auth
// src/auth.ts import NextAuth from "next-auth"; import authConfig from "./auth.config"; export const { handlers, signIn, signOut, auth } = NextAuth(authConfig);
Configuring auth.config
// src/auth.config.ts export default { providers: [ GitHub, ] adapter: DrizzleAdapter(db), } satisfies NextAuthConfig;
OAuth
Github OAuth
Environment Variables
// .env.local AUTH_GITHUB_ID= AUTH_GITHUB_SECRET=
Enable the Provider
// src/auth.config.ts providers: [ GitHub, ]
SignIn
// src/features/auth/components/github-button.tsx import { signIn } from "@/auth"; export function GithubButton() { return ( <form action={async () => { "use server"; await signIn("github"); }} > <button type="submit">Signin with GitHub</button> </form> ); }
Credentials
0. Utilities
Hash Password
// src/utils/password.ts import bcrypt from "bcryptjs"; export const saltAndHashPassword = (password: string) => { return bcrypt.hashSync(password, 10); }; export const verifyPassword = (password: string, hashedPassword: string) => { return bcrypt.compareSync(password, hashedPassword); };
Database utility
// src/utils/db.ts import { db } from "@/db/drizzle"; import { users } from "@/db/schema"; import { and, eq } from "drizzle-orm"; export const getUserFromDb = async (email: string, password: string) => { const user = await db .select() .from(users) .where(and(eq(users.email, email), eq(users.password, password))); return user.length > 0 ? user[0] : null; };
Zod Validation Schema
// src/lib/zod.ts import { object, string } from "zod"; export const signInSchema = object({ email: string({ required_error: "Email is required" }) .min(1, "Email is required") .email("Invalid email"), password: string({ required_error: "Password is required" }) .min(1, "Password is required") .min(8, "Password must be more than 8 characters") .max(32, "Password must be less than 32 characters"), });
1. provider
// src/auth.config.ts
import { signInSchema } from "@/lib/zod";
import { getUserFromDb } from "@/utils/db";
import { saltAndHashPassword } from "@/utils/password";
import Credentials from "next-auth/providers/credentials";
providers: [
Credentials({
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" },
},
authorize: async (credentials) => {
let user = null;
const validatedFields = signInSchema.safeParse(credentials);
if (!validatedFields.success) {
throw new Error("Invalid credentials.");
}
const { email, password } = validatedFields.data;
const pwHash = saltAndHashPassword(password);
user = await getUserFromDb(email, pwHash);
if (!user) {
throw new Error("Invalid credentials.");
}
return user;
},
}),
],
2. Signin Form
// src/features/auth/components/credential-form.tsx
import { signIn } from "@/auth";
export const CredentialForm = () => {
return (
<form
action={async (formData) => {
"use server";
await signIn("credentials", formData);
}}
>
<label>
Email
<input name="email" type="email" />
</label>
<label>
Password
<input name="password" type="password" />
</label>
<button>Sign In</button>
</form>
);
};