Panduan cepat: Next.js
Tambahkan proses masuk ke aplikasi Next.js dengan App Router. SDK @usher/nextjs segera hadir; sampai saat itu, panduan ini menggunakan alur OpenID Connect standar, persis seperti yang nanti dikerjakan SDK untuk Anda.
Sebelum memulai
- Aplikasi Next.js 15 atau 16 yang menggunakan App Router.
- Lingkungan Usher. Di Usher Console, buka proyek Anda, pilih lingkungan, lalu catat penerbit lingkungan tersebut.
Daftarkan aplikasi Anda
Di Console, buka Aplikasi lalu buat aplikasi web. Tambahkan URI pengalihan ini persis seperti tertulis:
http://localhost:3000/auth/callbackSalin ID klien dan rahasia klien. Rahasia hanya ditampilkan sekali.
Tambahkan variabel lingkungan
USHER_ISSUER=https://acme.usherid.com
USHER_CLIENT_ID=your_client_id
USHER_CLIENT_SECRET=your_client_secret
APP_URL=http://localhost:3000Pasang klien OpenID Connect
Klien bersertifikasi apa pun bisa dipakai. Panduan ini menggunakan openid-client, yang menangani discovery, PKCE, dan validasi token.
pnpm add openid-clientTemukan penerbit Anda
Discovery membaca endpoint dan kunci penerbit satu kali, sehingga tidak ada yang perlu ditulis langsung di kode.
import * as client from 'openid-client'
let config: Promise<client.Configuration> | undefined
export function usher() {
config ??= client.discovery(
new URL(process.env.USHER_ISSUER!),
process.env.USHER_CLIENT_ID!,
process.env.USHER_CLIENT_SECRET!,
)
return config
}
export const redirectUri = `${process.env.APP_URL}/auth/callback`Arahkan orang ke Lobby
Sebuah route handler membuat verifier PKCE dan nilai state, menyimpannya di cookie berumur pendek, lalu mengalihkan ke Lobby.
import * as client from 'openid-client'
import { cookies } from 'next/headers'
import { redirect } from 'next/navigation'
import { redirectUri, usher } from '@/lib/usher'
export async function GET() {
const config = await usher()
const codeVerifier = client.randomPKCECodeVerifier()
const state = client.randomState()
const jar = await cookies()
jar.set('usher_flow', JSON.stringify({ codeVerifier, state }), {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/auth',
maxAge: 600,
})
const url = client.buildAuthorizationUrl(config, {
redirect_uri: redirectUri,
scope: 'openid profile email offline_access',
code_challenge: await client.calculatePKCECodeChallenge(codeVerifier),
code_challenge_method: 'S256',
state,
})
redirect(url.href)
}Tangani callback
Usher mengembalikan orang tersebut dengan sebuah kode. Tukarkan kode itu dengan token; openid-client memeriksa state, verifier PKCE, dan tanda tangan token ID untuk Anda.
import * as client from 'openid-client'
import { cookies } from 'next/headers'
import { redirect } from 'next/navigation'
import { usher } from '@/lib/usher'
export async function GET(request: Request) {
const config = await usher()
const jar = await cookies()
const flow = JSON.parse(jar.get('usher_flow')?.value ?? '{}')
jar.delete('usher_flow')
const tokens = await client.authorizationCodeGrant(config, new URL(request.url), {
pkceCodeVerifier: flow.codeVerifier,
expectedState: flow.state,
})
const claims = tokens.claims()! // sub, email, name, …
// start your own session here: store claims.sub, and keep the refresh token server-side
redirect('/')
}Keluar
Akhiri sesi orang tersebut di aplikasi Anda, lalu arahkan mereka ke endpoint end-session Usher agar Lobby juga melupakan mereka.
import * as client from 'openid-client'
import { redirect } from 'next/navigation'
import { usher } from '@/lib/usher'
export async function GET() {
const config = await usher()
// clear your own session first
const url = client.buildEndSessionUrl(config, {
post_logout_redirect_uri: process.env.APP_URL!,
})
redirect(url.href)
}Dengan SDK
Saat @usher/nextjs dipublikasikan, aplikasi yang sama akan terlihat seperti ini. Pratinjau di bawah dapat berubah sebelum rilis.
// app/layout.tsx
import { UsherProvider } from '@usher/nextjs'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<UsherProvider>{children}</UsherProvider>
</body>
</html>
)
}
// app/page.tsx
import { SignIn, UserButton, currentUser } from '@usher/nextjs'
export default async function Page() {
const user = await currentUser()
return user ? <UserButton /> : <SignIn />
}