- Github & Live Website: https://www.codewithantonio.com/projects/auth-masterclass
- Auth.js: https://authjs.dev/
- Middleware config: https://dub.sh/Apr6dvD
- Resend: https://resend.com/
- Node.js: https://nodejs.org/en
- ShadcnUI: https://ui.shadcn.com/
- Clerk: https://dub.sh/SdVFxFU
Learn how to add advanced authentication to your NextJS App.
Key Features:
- ๐ Next-auth v5 (Auth.js)
- ๐ Next.js 14 with server actions
- ๐ Credentials Provider
- ๐ OAuth Provider (Social login with Google & GitHub)
- ๐ Forgot password functionality
- โ๏ธ Email verification
- ๐ฑ Two factor verification (2FA)
- ๐ฅ User roles (Admin & User)
- ๐ Login component (Opens in redirect or modal)
- ๐ Register component
- ๐ค Forgot password component
- โ Verification component
โ ๏ธ Error component- ๐ Login button
- ๐ช Logout button
- ๐ง Role Gate
- ๐ Exploring next.js middleware
- ๐ Extending & Exploring next-auth session
- ๐ Exploring next-auth callbacks
- ๐ค useCurrentUser hook
- ๐ useRole hook
- ๐ง currentUser utility
- ๐ฎ currentRole utility
- ๐ฅ๏ธ Example with server component
- ๐ป Example with client component
- ๐ Render content for admins using RoleGate component
- ๐ก๏ธ Protect API Routes for admins only
- ๐ Protect Server Actions for admins only
- ๐ง Change email with new verification in Settings page
- ๐ Change password with old password confirmation in Settings page
- ๐ Enable/disable two-factor auth in Settings page
- ๐ Change user role in Settings page (for development purposes only)
- actions: "use server" ๋ก ์๋ฒ ์ฌ์ด๋ ๊ธฐ๋ฐ ๋น๋๊ธฐ ์์ ์ ์คํ ํ ์ก์
- libs: ์์ฃผ ์ฌ์ฉํ๋ ๊ธฐ๋ฅ๋ค์ ๋ผ์ด๋ธ๋ฌ๋ฆฌํ
- data: ๋๋น ์ ๋ณด๋ฅผ ์ด์ฉํด์ ๋ฐ์ดํฐ ์ ๊ณต
- hooks: ํด๋ผ์ด์ธํธ ์ฌ์ด๋์์ ์ฌ์ฉ ํ hook
- routes.ts: public, auth ๋ฑ๋ฑ Route ๊ด๋ฆฌ
- next-auth.d.ts: next-auth ์์ ์ฌ์ฉ ํ๋ session ์ปค์คํ
- middleware.ts: ์ธ์ฆ ์ ๋ฆฌ๋๋ ํธ ๊ด๋ฆฌ
- auth.ts: ๋ก๊ทธ์ธ, ์ธ์ , ์ธ์ฆ ์ฒ๋ฆฌ
- auth.config.ts: ์ธ์ฆ ํ๋ก๋ฐ์ด๋ ์ ๊ณต ๋ฐ ์ธ์ฆ ๋ก์ง
ํ๋ก์ ํธ ์์ฑ - create-next-app
npx create-next-app@latest auth-tutorial
Need to install the following packages:
create-next-app@14.1.0
Ok to proceed? (y) y
โ Would you like to use TypeScript? โฆ No / Yes
โ Would you like to use ESLint? โฆ No / Yes
โ Would you like to use Tailwind CSS? โฆ No / Yes
โ Would you like to use `src/` directory? โฆ No / Yes
โ Would you like to use App Router? (recommended) โฆ No / Yes
โ Would you like to customize the default import alias (@/*)? โฆ No / Yes
Creating a new Next.js app in /Users/user/Documents/Github/auth-tutorial.
Using npm.
Initializing project with template: app-tw
Installing dependencies:
- react
- react-dom
- next
Installing devDependencies:
- typescript
- @types/node
- @types/react
- @types/react-dom
- autoprefixer
- postcss
- tailwindcss
- eslint
- eslint-config-next
added 360 packages, and audited 361 packages in 24s
128 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
Initialized a git repository.
Success! Created auth-tutorial at /Users/user/Documents/Github/auth-tutorial
- shadcn-ui ์ถ๊ฐ
npx shadcn-ui@latest init
โ Which style would you like to use? โบ New York
โ Which color would you like to use as base color? โบ Slate
โ Would you like to use CSS variables for colors? โฆ no / yes
โ Writing components.json...
โ Initializing project...
โ Installing dependencies...
Success! Project initialization completed. You may now add components.
npx shadcn-ui@latest add button
- ๋ผ์ฐํฐ๋ ์ธ๋ฑ์ค๋ผ๋ ์ด๋ฆ์ ํ์ผ์ ๋๋ ํ ๋ฆฌ์ ๋ฃจํธ๋ก ์๋ ๋ผ์ฐํ ํฉ๋๋ค.
- pages/index.js โ /
- pages/blog/index.js โ /blog
- ๋ผ์ฐํฐ๋ ์ค์ฒฉ ํ์ผ์ ์ง์ํฉ๋๋ค. ์ค์ฒฉ๋ ํด๋ ๊ตฌ์กฐ๋ฅผ ๋ง๋ค๋ฉด ํ์ผ์ด ์๋์ผ๋ก ๋์ผํ ๋ฐฉ์์ผ๋ก ๋ผ์ฐํ ๋ฉ๋๋ค.
pages/blog/first-post.js โ /blog/first-post
pages/dashboard/settings/username.js โ /dashboard/settings/username
- app/globals.css ์์
- ๊ธฐ๋ณธ ๋์ด 100% css ์ถ๊ฐ
- app/page.tsx ์์
- ๊ธฐ๋ณธ ํ๋ฉด ๊ตฌ์ฑ ๋ฐ ๋ก๊ทธ์ธ ๋ฒํผ ์ถ๊ฐ
- components/auth/login-button.tsx ์์ฑ
- ๋ก๊ทธ์ธ ๋ฒํผ ๋ํ ์ปดํฌ๋ํธ
- ํด๋ฆญ ์ปจํธ๋กค
Gradient ์ ์ฉ ๋ฐฉ๋ฒ
<main className="bg-[radial-gradient(ellipse_at_top,_var(--tw-gradient-stops))] from-sky-400 to-blue-800">
- top ์ชฝ์ from-sky-400 ๋ถํฐ ์๋๋ก ์์ด ์งํด์ง
- ์์ชฝ ์ฌ์ด๋๋ ์์ชฝ์์ ๋ถํฐ ๋ฐ๊นฅ์ผ๋ก from-sky-400 -> to-blue-800 ์ผ๋ก ์งํด์ง
์นด๋ ํํ์ ๋ก๊ทธ์ธ ์ปดํฌ๋ํธ ๊ตฌํ
- app/auth/login/page.tsx
- ๋ก๊ทธ์ธ ํผ
- app/auth/layout.tsx
- ๋ก๊ทธ์ธ ๋ ์ด์์
- components/auth/login-form.tsx
- ๋ก๊ทธ์ธ ํผ ์์ญ ๊ตฌ์ฑ
- components/auth/card-wrapper.tsx
- ์นด๋๋ฅผ ๊ฐ์ธ๋ ๋ํผ
- components/auth/back-buttons.tsx
- ๋ค๋ก๊ฐ๊ธฐ ๋ฒํผ ์ปดํฌ๋ํธ
- components/auth/header.tsx
- ํค๋ ์ปดํฌ๋ํธ
- components/auth/social.tsx
- ์์ ๋ก๊ทธ์ธ ๋ฒํผ
npx shadcn-ui@latest add card
- ์นด๋ ์ปดํฌ๋ํธ
npm i react-icons
- ๋ฆฌ์กํธ ์์ด์ฝ ํฉ
- schemas/index.ts ์ถ๊ฐ
- zod ์คํค๋ง ์์ฑ
- components/auth/login-form.tsx ์์
- form ์ถ๊ฐ ๋ฐ useForm
- components/form-error.tsx ์ถ๊ฐ
- form ์์ฑ ์คํจ์ ๋ณด์ฌ์ค ์ปดํฌ๋ํธ
- components/form-success.tsx ์ถ๊ฐ
- form ์์ฑ ์ฑ๊ณต์ ๋ณด์ฌ์ค ์ปดํฌ๋ํธ
npx shadcn-ui@latest add form
npx shadcn-ui@latest add input
zod
- Zod๋ TypeScript์ ์ ์ด์ธ๋ฆฌ๋ ์คํค๋ง ์ ์ธ ๋ฐ ๊ฒ์ฆ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค.
- Zod๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฐ์ดํฐ ํ์ ์ ํ ๋ฒ ์ ์ธํ๋ฉด Zod๊ฐ ์๋์ผ๋ก TypeScript ํ์ ์ ์ถ๋ก ํด์ค๋๋ค.
- Zod๋ ๊ฐ๋จํ ํ์ ์ ๋ณต์กํ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ก ์ฝ๊ฒ ์กฐํฉํ ์ ์์ต๋๋ค.
- React Hook Form์ React์์ ์์ฃผ ์ฌ์ฉ๋๋ "์ฑ๋ฅ, ์ ์ฐ์ฑ, ํ์ฅ์ฑ์ด ๋ฐ์ด๋ ํผ ๋ผ์ด๋ธ๋ฌ๋ฆฌ"์ ๋๋ค.
- React Hook Form์ Zod์ ํจ๊ป ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, Zod๋ฅผ ํตํด ํผ ๋ฐ์ดํฐ์ ์ ํจ์ฑ์ ๊ฒ์ฌํ ์ ์์ต๋๋ค.
- Next.js 13์์๋ Server Actions๋ผ๋ ์๋ก์ด ๊ธฐ๋ฅ์ด ์ถ๊ฐ๋์๋๋ฐ, ์ด๋ ์๋ฒ์์ ํผ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๊ณ ์๋ต์ ๋ณด๋ด๋ ๋ฐ ์ ์ฉํฉ๋๋ค.
- actions/login.ts ์ถ๊ฐ
- ์คํค๋ง ์ ํจ์ฑ ๊ฒ์ฌ์ ๋ฐ๋ผ์ ์ฑ๊ณต/์คํจ ๋ฉ์์ง ๋ฆฌํด
- actions/register.ts ์ถ๊ฐ
- ์คํค๋ง ์ ํจ์ฑ ๊ฒ์ฌ์ ๋ฐ๋ผ์ ์ฑ๊ณต/์คํจ ๋ฉ์์ง ๋ฆฌํด
- components/auth/login-form.tsx ์์
- ๋ก๊ทธ์ธ action ์ฐ๊ฒฐ
- success, error ์ฐ๊ฒฐ
- components/auth/register-form.tsx ์ถ๊ฐ
- ํ์๊ฐ์ ํผ ์ปดํฌ๋ํธ
- schemas/index.ts ์์
- RegisterSchema ์ ํจ์ฑ ๊ฒ์ฌ ์ถ๊ฐ
- lib/db.ts ์์ฑ
- prisma ํด๋ผ์ด์ธํธ ์์ฑ
- Prisma ์ด๊ธฐ ์ค์
npx prisma init
- Database ์ฐ๋
- ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ Neon Serverless Postgres ์ฌ์ฉ
- Neon ํ๋ก์ ํธ ์์ฑ
- prisma/schema.prisma ์ค์
- database ์ ๋ณด ์ค์ (Neon postgre ์ฌ์ฉ)
- model User ์์ฑ
npx prisma generate
- Prisma ํด๋ผ์ด์ธํธ ์คํค๋ง ์์ฑnpx prisma db push
- ๋ฐ์ดํฐ๋ฒ ์ด์ค์ Prisma ์คํค๋ง ๋๊ธฐํ
- auth/prisma-adapter ์ฐ๊ฒฐ
- prisma/schema.prisma ์์
- User ์์
- model Account ์ถ๊ฐ
- prisma/schema.prisma ์์
npm i -D prisma
npm i @prisma/client
npm i @auth/prisma-adapter
- actions/register.ts ์์
- ํ์๊ฐ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ
- bcryptjs ๋ฅผ ์ฌ์ฉํด์ ๋น๋ฐ๋ฒํธ hash ์ฒ๋ฆฌ
- ์ค๋ณต ๊ฐ์ ์๋ฌ ์ฒ๋ฆฌ
- data/user.ts ์์ฑ
- email, id ๋ก User ๋ฐ์ดํฐ๋ฅผ ๋ฐํํ๋ ๋ฐ์ดํฐ ์ถ๊ฐ
npm i bcryptjs
npm i -D @types/bcryptjs
npm install next-auth@beta
- auth.ts ์์ฑ
- app/api/auth/[...nextauth]/route.ts ์์ฑ
- http://localhost:3000/api/auth/providers ํ๋ก๋ฐ์ด๋ ํ์ธ
- AUTH_SECRET ์์ฑ
npx auth secret
- .env
- ์์ฑ๋ AUTH_SECRET ์ถ๊ฐ
- AUTH_SECRET ์์ฑ
- middleware ์ถ๊ฐ
- middleware.ts ์์ฑ
- Edge compatibility
- ์ธ์ฆ ๊ตฌ์ฑ ๋ถํ
- NextAuth.js๋ ๋ ๊ฐ์ง ์ธ์ ์ ๋ต์ ์ง์ํฉ๋๋ค. ์ด๋ํฐ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์ธ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ๋๋ก ์ ํํ ์ ์์ต๋๋ค. ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฐ ํด๋น ์ด๋ํฐ๊ฐ Edge ๋ฐํ์/์ธํ๋ผ์ ํธํ๋์ง ์๋ ํ "๋ฐ์ดํฐ๋ฒ ์ด์ค" ์ธ์ ์ ๋ต์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
auth.config.ts
์์ฑ- next-auth ์์ ์ ๊ณตํ๋ credentila ํจ์์ authorize ์ฝ๋ฐฑ์ ํตํด์ DB ์ ์ ์ ์ ๋ณด์ ๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ๋์ง ๊ฒ์ฆ
auth.ts
์์ - authConfig, PrismaAdapter ๋ฅผ ์ฌ์ฉํด์ ์ธ์ฆํ๋๋ก ์์
npm i @auth/prisma-adapter
์ด๋ํฐ ์ค์น
- ๋ฏธ๋ค์จ์ด๊ฐ ์ฃ์ง์ ํธํ๋์ง ์๋ ์ด๋ํฐ๋ก ๊ฐ์ ธ์ค๊ธฐ๋ฅผ ์ฌ์ฉํ๊ณ ์์ง ์์์ง ํ์ธํฉ๋๋ค:
- export { auth as middleware } from './auth' + import authConfig from "./auth.config" + import NextAuth from "next-auth" + export const { auth: middleware } = NextAuth(authConfig)
app/(protected)/settings/page.tsx
์์ฑ- ์ธ์ฆ ํ ์คํธ ํ์ด์ง ์ถ๊ฐ
routes.ts
์์ฑ- publicRoutes, apiRoutes ๋ฑ route ๊ด๋ จ ์ ๋ณด ์ ๊ณต
middleware.ts
์์ - auth ์ฝ๋ฐฑ ์์
- ๋ก๊ทธ์ธ ์ ๋ฌด์ ๋ฐ๋ผ์ ๋ฆฌ๋๋ ํธ ์ฒ๋ฆฌ
- auth ์ฝ๋ฐฑ ์์
auth.ts
์์ - signIn, signOut ์ถ๊ฐ
actions/login.ts
์์ - ๋ก๊ทธ์ธ ์ก์ ์ next-auth ์ signIn ์ credentials ํ๋ก๋ฐ์ด๋๋ก ๋ก๊ทธ์ธ ์ฒ๋ฆฌ
- ๋ก๊ทธ์ธ ํ settings ํ์ด์ง ๋ฆฌ๋๋ ํธ
app/(protected)/settings/page.tsx
์์ - ๋ก๊ทธ์์ ๊ตฌํ
- ๋ก๊ทธ์์ ํ ๋ก๊ทธ์ธ ํ์ด์ง ๋ฆฌ๋๋ ํธ
auth.ts
์์ - NextAuth Callbacks ์ถ๊ฐ
- session, jwt
- NextAuth Callbacks ์ถ๊ฐ
prisma/schema.prisma
์์ - UserRole ์ถ๊ฐ (ADMIN, USER)
npx prisma generate
npx prisma migrate reset
npx prisma db push
next-auth.d.ts
์์ฑ
NextAuth - Callbacks
- ์ฝ๋ฐฑ์ ๋์์ด ์ํ๋ ๋ ๋ฐ์ํ๋ ์ํฉ์ ์ ์ดํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ ๋น๋๊ธฐ ํจ์์ ๋๋ค.
- ์ฝ๋ฐฑ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ด ์ก์ธ์ค ์ ์ด๋ฅผ ๊ตฌํํ๊ณ ์ธ๋ถ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋๋ API์ ํตํฉํ ์ ์๊ธฐ ๋๋ฌธ์ ํนํ JSON ์น ํ ํฐ๊ณผ ๊ด๋ จ๋ ์๋๋ฆฌ์ค์์ ๋งค์ฐ ๊ฐ๋ ฅํฉ๋๋ค.
auth.config.ts
์์ - Provider ์ GitHub, Google ํ๋ก๋ฐ์ด๋ ์ถ๊ฐ
- clientId, clientSecret ์ถ๊ฐ(
.env
์ ์ค์ ๊ฐ์ ์ถ๊ฐ)
- clientId, clientSecret ์ถ๊ฐ(
http://localhost:3000/api/auth/providers
ํ๋ก๋ฐ์ด๋ ํ์ธ- providers GitHub clientId, clientSecret ์ถ๊ฐ
- GitHub ์์ OAuth ์ค์ ํ ํค ๊ฐ์ ธ์ค๊ธฐ
- providers Google clientId, clientSecret ์ถ๊ฐ
- Google ์์ OAuth ์ค์ ํ ํค ๊ฐ์ ธ์ค๊ธฐ
- Provider ์ GitHub, Google ํ๋ก๋ฐ์ด๋ ์ถ๊ฐ
components/auth/social.tsx
์์ - ์์ ๋ก๊ทธ์ธ์ ์ง์ํ๋๋ก ์์ ๋ก๊ทธ์ธ ๋ฒํผ onClick ์์
- ์์
๊ณ์ ๊ณผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฌ์ฉ์์์ ์ฐ๊ฒฐ
auth.ts
์์ - events: linkAccount ์ถ๊ฐ
- ๋ฐ์ดํฐ๋ฒ ์ด์ค
emailVerifed
ํ๋ ๋ฐ์ดํฐ ์ฑ์์ง
- ๋ฐ์ดํฐ๋ฒ ์ด์ค
- pages ์ถ๊ฐ
- ๋ก๊ทธ์ธ, ์๋ฌ path ์ง์
app/auth/error/page.tsx
์๋ฌ ํ์ด์ง ์ถ๊ฐcomponents/auth/error-card.tsx
์๋ฌ ์ปดํฌ๋ํธ ์นด๋ ์ถ๊ฐ
- events: linkAccount ์ถ๊ฐ
components/auth/login-form.tsx
์์ - ์์ ๊ณ์ ์ด๋ฉ์ผ ์ค๋ณต์ ์๋ฌ ์ถ๊ฐ (OAuthAccountNotLinked ์๋ฌ)
- GitHub(login) > Settings > Developer Settings > OAuth Apps > Register a new application
- Application name: auth-tutorial
- Homepage URL: http://localhost:3000
- Authorization callback URL: http://localhost:3000/api/auth/callback/github
http://localhost:3000/api/auth/providers
์ฌ๊ธฐ์ github callbackUrl ์ ๋ณต์ฌ
- ์๋ก์ด ์ดํ๋ฆฌ์ผ์ด์ ์ด ์์ฑ๋๋ฉด clientId๋ฅผ ๋ฐ์ ์ ์์ต๋๋ค.
- Client secrets > Generate a new client secret ์ผ๋ก Secret ์์ฑ
- google api console ๊ฒ์
- ์๋จ ํ๋ก์ ํธ ์ ํ > ์ ํ๋ก์ ํธ > ํ๋ก์ ํธ ์์ฑ
- APIs & Services ๊ฒ์ or ๋ฉ๋ด ํด๋ฆญ > OAuth consent screen(OAuth ๋์ ํ๋ฉด)
- External(์ธ๋ถ) ํ์ ์ ํ > ๋ง๋ค๊ธฐ
- ์ฑ ์ด๋ฆ, ์ฌ์ฉ์ ์ง์ ์ด๋ฉ์ผ, ๊ฐ๋ฐ์ ์ฐ๋ฝ์ฒ ์ ๋ณด ๋ฑ ํ์ ์ ๋ณด ์ ๋ ฅ ํ ์งํ
- ๋๋จธ์ง ๋จ๊ณ๋ ๋ณ๋ค๋ฅธ ์ ๋ ฅ์์ด ํต๊ณผ
- Credentials
- CREATE CREDENTIALS > OAuth client ID
- Application type: Web application
- Name: web client 1
- Authorized JavaScript origins: http://localhost:3000
- Authorized redirect URIs: http://localhost:3000/api/auth/callback/google
http://localhost:3000/api/auth/providers
์ฌ๊ธฐ์ google callbackUrl ์ ๋ณต์ฌ
- CREATE CREDENTIALS > OAuth client ID
- OAuth client created - Client ID, Client secret ๋ง๋ค๊ธฐ ์๋ฃ
- ํ ์คํธ๋ฅผ ํ๊ธฐ ์ํด์๋ ํ ์คํธ ์ฌ์ฉ์ ์ถ๊ฐ ํ์
OAuthAccountNotLinked ์๋ฌ ๋ฐ์(์์ ๋ก๊ทธ์ธ ์ด๋ฉ์ผ ์ค๋ณต)
- Social ๊ณ์ ์ผ๋ก ๋ก๊ทธ์ธ ์ ๊ฐ์ ์ด๋ฉ์ผ์ ์ฌ์ฉํ ๊ฒฝ์ฐ
OAuthAccountNotLinked
์๋ฌ๊ฐ ๋ฐ์- ๋ฐ๋ก ์ฒ๋ฆฌํ๋ ค๋ฉด ๋ณ๋์ ํ์ด์ง๋ฅผ ๊ตฌํํด์ ์ฒ๋ฆฌ
- ํ ์คํธ์์๋ ๋๋น์ ๋ฐ์ดํฐ๋ฅผ ์ญ์ ํ ํ ์คํธ
- prisma/schema.prisma ์์
- VerificationToken ๋ชจ๋ธ ์ถ๊ฐ
- data/verification-token.ts ์์ฑ
- ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ email, token ์ผ๋ก verification token ๊ฐ์ ธ์ค๊ธฐ
- lib/tokens.ts ์์ฑ
- ์๋ก์ด ํ ํฐ ์์ฑ(๊ธฐ์กด ํ ํฐ์ด ์์ผ๋ฉด ์ญ์ )
npm i uuid
์ค์นfrom "uuid"
๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํดnpm i --save-dev @types/uuid
์ค์น
- actions/register.ts ์์
- ํ์ ๊ฐ์ ์ ์ธ์ฆ ํ ํฐ ์์ฑ
- actions/login.ts ์์
- ๋ก๊ทธ์ธ์ ์ด๋ฉ์ผ ์ธ์ฆ์ด ์๋์ด ์์ ๊ฒฝ์ฐ ํ ํฐ ์ฌ์์ฑ ํ ๋ฉ์์ง๋ง ์ ๋ฌ
- lib/mail.ts ์์ฑ
- resend ๋ฅผ ์ฌ์ฉํด์ ์ด๋ฉ์ผ ์ ์ก(ํ ํฐ ์ธ์ฆ)
- actions/register.ts | actions/login.ts ์์
- ํ์๊ฐ์ /๋ก๊ทธ์ธ ์ ์ด๋ฉ์ผ ์ ์ก
- ์ด๋ฉ์ผ ์ ์ก ์๋น์ค
- ํ์๊ฐ์ > ํ ๋ง๋ค๊ธฐ > API Key ์ถ๊ฐ
npm install resend
์ค์น.env
RESEND_API_KEY ์ถ๊ฐ
npm i uuid
npm i --save-dev @types/uuid
routes.ts
์์ - public routes ์ ์ด๋ฉ์ผ ์ธ์ฆ์ ์ฌ์ฉํ๋ ๊ฒฝ๋ก ์ถ๊ฐ
app/auth/new-verification/page.tsx
์์ฑ- ์ด๋ฉ์ผ ์ธ์ฆ ํ์ด์ง
components/auth/new-verification-form.tsx
์์ฑ- ์ด๋ฉ์ผ ์ธ์ฆ ํผ
- searchParmas ์ token ์ผ๋ก ์๋ ์ธ์ฆ ์ฒ๋ฆฌ
- success, error ๋ฉ์์ง ์ฒ๋ฆฌ
actions/new-verification.ts
์์ฑ- ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ํฐ ์ ๋ณด์ ๋ฐ๋ผ ์ ํจ์ฑ ์ฒดํฌ
- ์ ํจํ ํ ํฐ์ด๋ฉด ์ ์ ์ ๋ณด ์ ๋ฐ์ดํธ
npm i react-spinners
components/auth/login-form.tsx
์์ - ๋น๋ฐ๋ฒํธ ์ฌ์ค์ ๋ฒํผ ์ถ๊ฐ
- ๋น๋ฐ๋ฒํธ ์ฌ์ค์ ํ๋ฉด ์ด๋
routes.ts
์์ - authRoutes ์ ๋น๋ฐ๋ฒํธ ์ฌ์ค์ path ์ถ๊ฐ
app/auth/reset/page.tsx
์์ฑ- ๋น๋ฐ๋ฒํธ ์ฌ์ค์ ํ๋ฉด
components/auth/reset-form.tsx
์์ฑ- ๋น๋ฐ๋ฒํธ ์ฌ์ค์ ํผ
schemas/index.ts
์์ - ResetSchema ์ถ๊ฐ
actions/reset.ts
์์ฑ- ์ด๋ฉ์ผ ์ฌ์ ์ก์ ์ํ ์ก์
- ์ด๋ฉ์ผ ์ ํจ์ฑ ์ฒดํฌ
- ์ด๋ฉ์ผ ์ฌ์ ์ก
prisma/schema.prisma
์์ - PasswordResetToken ๋ชจ๋ธ ์ถ๊ฐ
data/password-reset-token.ts
์์ฑ- ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ passwordResetToken ๋ชจ๋ธ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ์ญํ
lib/tokens.ts
์์ - passwordResetToken ์์ ์ฌ์ฉ ํ ํ ํฐ ๋ฐ ์ ํจ๊ธฐ๊ฐ ๋ฐ์ดํฐ ์์ฑ
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ํฐ ๋ฐ์ดํฐ ์ต์ ํ
lib/mail.ts
์์ - ํจ์ค์๋ ์ฌ์ค์ ์ด๋ฉ์ผ ๋ฐ์ก ์ถ๊ฐ
- routes.ts ์์
- authRoutes ์
/auth/new-password
์ถ๊ฐ
- authRoutes ์
app/auth/new-password/page.tsx
์์ฑ- ํจ์ค์๋ ์ฌ์ค์ ํ๋ฉด
components/auth/new-password-form.tsx
์์ฑ- ํจ์ค์๋ ์ฌ์ค์ ํ๋ฉด ํผ
schemas/index.ts
์์ - NewPasswordSchema ์ถ๊ฐ
actions/new-password.ts
์์ฑ- ๋น๋ฐ๋ฒํธ ์ฌ์ค์ ํ๋ฉด์์ ์ฌ์ฉ ํ ๋ฒํผ ์ก์
- ์ ํจ์ฑ ์ฒดํฌ(๊ธธ์ด, ํ ํฐ ๋ง๋ฃ, ํ ํฐ ํ๋ผ๋ฏธํฐ ๋ฑ)
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ์ ์ ๋ฐ์ดํธ, ์ฌ์ฉ ํ ํ ํฐ ์ ๊ฑฐ
prisma/schema.prisma
์์ - TwoFactorConfirmation ๋ชจ๋ธ ์ถ๊ฐ
- TwoFactorToken ๋ชจ๋ธ ์ถ๊ฐ
- User ๋ชจ๋ธ์ twoFactor ๊ด๋ จ ํ๋ ์ถ๊ฐ
- prisma
npx prisma generate
npx prisma migrate reset
npx prisma db push
data/two-factor-token.ts
์์ฑ- token, email ๋ก 2๋จ๊ณ ํ ํฐ ์ ๋ณด ๊ฐ์ ธ์ด
data/two-factor-confirmation.ts
์์ฑ- ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ 2๋จ๊ณ ์ธ์ฆ ์ ๋ณด ๊ฐ์ ธ์ด
lib/tokens.ts
์์ - 2 ๋จ๊ณ ์ธ์ฆ์์ ์ฌ์ฉํ ํ ํฐ ์์ฑ
- crypto ๋ด์ฅ ํจ์ ์ฌ์ฉํด์ ํ ํฐ ์์ฑ
lib/mail.ts
์์ - 2๋จ๊ณ ์ธ์ฆ ๋ฉ์ผ ์ ์ก
auth.ts
์์ - signIn ์ฝ๋ฐฑ์ 2FA ์ธ์ฆ ์ ๋ฐ์ดํธ
actions/login.ts
์์ - ๋ก๊ทธ์ธ์ 2FA ํ์ฑ ์ํ ์ฒดํฌ ํ ๋ถ๊ธฐ
- 2FA ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๋ณด ์ญ์ ๋ฐ ์์ธ์ฒ๋ฆฌ ์ถ๊ฐ
components/auth/login-form.tsx
์์ - 2FA ํ์ฑ/๋นํ์ฑํ UI ๋ณ๊ฒฝ
- 2FA ํ์ฑ ์ํ์ผ๊ฒฝ์ฐ ์ด๋ฉ์ผ๋ก ์ฝ๋๋ฅผ ๋ฐ์์ ๋ก๊ทธ์ธ
- app/(protected)/settings/page.tsx ์์
- useSession ํ ์ ํตํด์ ์ธ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋๋ก ์์
- app/layout.tsx ์์
- Root ์ SessionProvider ์ถ๊ฐ session ์ ๋ณด ์ธํ
- actions/logout.ts ์์ฑ
- ๋ก๊ทธ์์ ์ฒ๋ฆฌ
- app/(protected)/layout.tsx ์์ฑ
- navbar ๋ฅผ ๊ฐ์ง ์์ ๋ ์ด์์ ์ถ๊ฐ
- app/(protected)/_components/navbar.tsx ์์ฑ
- ์ค์ ๋ฉ๋ด ๋ค๋น๊ฒ์ด์ ๋ฒํผ
- hooks/use-current-user.ts ์์ฑ
- useSession ์ผ๋ก ํ์ฌ ์ ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋ ํ ์์ฑ
- components/auth/logout-button.tsx ์์ฑ
- ๋ก๊ทธ์์ ์ปดํฌ๋ํธ
- components/auth/user-button.tsx ์์ฑ
- ์ ์ ์๋ฐํ ์ปดํฌ๋ํธ
- ๋๋กญ๋ค์ด ๋ฉ๋ด ๊ตฌํ
- ๋ก๊ทธ์์
npx shadcn-ui@latest add dropdown-menu
npx shadcn-ui@latest add avatar
app/(protected)/server/page.tsx
์์ฑ- ๋ค๋น๊ฒ์ด์ Server ์์ ๋ณด์ฌ์ค ์ ์ ์ ๋ณด ํ๋ฉด
components/user-info.tsx
์์ฑ- ์ ์ ์ ๋ณด ์ปดํฌ๋ํธ
- shadcn-ui Badge ์ถ๊ฐ
- variant.success ํ์ ์ถ๊ฐ
auth.ts
์์ - jwt: ํ ํฐ ์ ๋ณด์ isTwoFactorEnabled ์ ๋ณด ์ ๋ฌ
- session: ํ ํฐ ์ ๋ณด๋ก session.user.isTwoFactorEnabled ์ค์
next-auth.d.ts
์์ - ExtendedUser.isTwoFactorEnabled ์ถ๊ฐ
app/(protected)/client/page.tsx
์์ฑ- ์๋ฒ์ ๋์ผํ ์ปดํฌ๋ํธ๋ฅผ ๋ณต์ฌํด์ Client ์ ์ ์ ๋ณด ํ๋ฉด ์ถ๊ฐ
npx shadcn-ui@latest add badge
app/(protected)/admin/page.tsx
์์ฑ- ๊ด๋ฆฌ์๋ง ์ฌ์ฉํ๋ ํ๋ฉด
- role-gate ๋ก ๊ถํ ๋ฉ์์ง ํ์
- ๊ด๋ฆฌ์์ฉ ์ก์ ์์ฑ
hooks/use-current-role.ts
์์ฑ- role ๊ฐ์ ธ์ค๋ hook ์์ฑ
components/auth/role-gate.tsx
์์ฑ- ๊ถํ ๋ฉ์์ง ์ปดํฌ๋ํธ
app/api/admin/route.ts
์์ฑ- ๊ด๋ฆฌ์๊ฐ ์ฌ์ฉ ํ API
app/layout.tsx
์์ - ์์์ ์ถ๊ฐ (sonner)
npx shadcn-ui@latest add sonner
: toast
schemas/index.ts
์์ - SettingsSchema ์ถ๊ฐ
app/(protected)/settings/page.tsx
์์ - ์นด๋ ํํ ์ปดํฌ๋ํธ ์ถ๊ฐ
- ์ ์ ์ด๋ฆ ์ ๋ฐ์ดํธ
actions/settings.ts
์์ฑ- ์ธ์ ์ ์ ์ฅ๋ ์ ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์์ ๋๋น ์ ์ ์ ๋ณด ์กฐํ ํ name ์ ๋ฐ์ดํธ
auth.ts
์์ - jwt ์ธ์ฆ์ ํ ํฐ ์ ๋ณด(name, email) ์ถ๊ฐ
data/account.ts
์์ฑ- ์ ์ ๊ณ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋ API
next-auth.d.ts
์์ - isOAuth ํ๋ ์ถ๊ฐ
npx shadcn-ui@latest add switch
npx shadcn-ui@latest add select
- Dialog ์ถ๊ฐ
- actions/login.ts ์์
- ์ฝ๋ฐฑ URL ๋ฆฌ๋๋ ํธ ์ ์ฉ
middleware.ts
์์ฑ- ์ฝ๋ฐฑ URL ์ถ๊ฐ
components/auth/login-button.tsx
์์ - modal Dialog ์ง์
components/auth/login-form.tsx
์์ - ๋ก๊ทธ์ธ ์ก์ ์์
- components/auth/social.tsx ์์
- ์ฝ๋ฐฑ URL ๋ฐ์์ ์ฒ๋ฆฌ
- lib/mail.ts ์์
- ํ๋์ฝ๋ฉ ๋๋ฉ์ธ ๋ณ๊ฒฝ
- GitHub, Google API Authorized domain ๋ณ๊ฒฝ
npx shadcn-ui@latest add dialog