feat: zitadel oidc auth with pkce, iron-session, middleware
This commit is contained in:
33
src/app/api/auth/callback/route.ts
Normal file
33
src/app/api/auth/callback/route.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { getSession } from '@/lib/auth/session'
|
||||
import { exchangeCode, verifyIdToken, isAllowedUser } from '@/lib/auth/zitadel'
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const url = new URL(req.url)
|
||||
const code = url.searchParams.get('code')
|
||||
const state = url.searchParams.get('state')
|
||||
if (!code || !state) return NextResponse.json({ error: 'missing code/state' }, { status: 400 })
|
||||
|
||||
const session = await getSession()
|
||||
const pending = session.loginInProgress
|
||||
if (!pending || pending.state !== state) {
|
||||
return NextResponse.json({ error: 'state mismatch' }, { status: 400 })
|
||||
}
|
||||
|
||||
const redirectUri = `${process.env.NEXT_PUBLIC_BASE_URL}/api/auth/callback`
|
||||
const tokens = await exchangeCode({ code, codeVerifier: pending.codeVerifier, redirectUri })
|
||||
const claims = await verifyIdToken(tokens.id_token)
|
||||
|
||||
if (!isAllowedUser(claims.sub)) {
|
||||
session.destroy()
|
||||
return NextResponse.json({ error: 'user not allowed' }, { status: 403 })
|
||||
}
|
||||
|
||||
session.userId = claims.sub
|
||||
session.email = claims.email
|
||||
session.name = claims.name
|
||||
delete session.loginInProgress
|
||||
await session.save()
|
||||
|
||||
return NextResponse.redirect(new URL('/', req.url))
|
||||
}
|
||||
18
src/app/api/auth/login/route.ts
Normal file
18
src/app/api/auth/login/route.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { getSession } from '@/lib/auth/session'
|
||||
import { generateVerifier, challengeFromVerifier, generateState } from '@/lib/auth/pkce'
|
||||
import { buildAuthorizeUrl } from '@/lib/auth/zitadel'
|
||||
|
||||
export async function GET() {
|
||||
const verifier = generateVerifier()
|
||||
const challenge = challengeFromVerifier(verifier)
|
||||
const state = generateState()
|
||||
const redirectUri = `${process.env.NEXT_PUBLIC_BASE_URL}/api/auth/callback`
|
||||
|
||||
const session = await getSession()
|
||||
session.loginInProgress = { state, codeVerifier: verifier }
|
||||
await session.save()
|
||||
|
||||
const url = buildAuthorizeUrl({ state, codeChallenge: challenge, redirectUri })
|
||||
return NextResponse.redirect(url)
|
||||
}
|
||||
9
src/app/api/auth/logout/route.ts
Normal file
9
src/app/api/auth/logout/route.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { getSession } from '@/lib/auth/session'
|
||||
import { buildEndSessionUrl } from '@/lib/auth/zitadel'
|
||||
|
||||
export async function GET() {
|
||||
const session = await getSession()
|
||||
session.destroy()
|
||||
return NextResponse.redirect(buildEndSessionUrl(`${process.env.NEXT_PUBLIC_BASE_URL}/`))
|
||||
}
|
||||
Reference in New Issue
Block a user