Mula pantas: Next.js
Tambah daftar masuk pada aplikasi Next.js dengan App Router. SDK @usher/nextjs akan datang; sehingga ia tiba, panduan ini menggunakan aliran OpenID Connect standard, iaitu tepat apa yang akan dilakukan oleh SDK untuk anda.
Sebelum anda bermula
- Aplikasi Next.js 15 atau 16 yang menggunakan App Router.
- Persekitaran Usher. Dalam Usher Console, buka projek anda, pilih persekitaran, dan catat pengeluarnya.
Daftarkan aplikasi anda
Dalam Console, pergi ke Aplikasi dan cipta aplikasi web. Tambah URI ubah hala ini dengan tepat:
http://localhost:3000/auth/callbackSalin ID klien dan rahsia klien. Rahsia itu ditunjukkan sekali sahaja.
Tambah pemboleh ubah persekitaran anda
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
Mana-mana klien yang diperakui berfungsi. Panduan ini menggunakan openid-client, yang mengendalikan penemuan, PKCE dan pengesahan token.
pnpm add openid-clientTemui pengeluar anda
Penemuan membaca titik akhir dan kunci pengeluar sekali, jadi tiada apa-apa yang dikod keras.
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`Hantar orang ke Lobby
Pengendali laluan membuat pengesah PKCE dan nilai state, menyimpannya dalam kuki berjangka hayat singkat, dan mengubah hala 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)
}Kendalikan panggilan balik
Usher menghantar orang itu kembali dengan kod. Tukarkannya dengan token; openid-client menyemak state, pengesah PKCE dan tandatangan 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('/')
}Daftar keluar
Tamatkan sesi orang itu dalam aplikasi anda, kemudian hantar mereka ke titik akhir tamat sesi Usher supaya 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
Apabila @usher/nextjs diterbitkan, aplikasi yang sama kelihatan seperti ini. Pratonton di bawah mungkin berubah sebelum keluaran.
// 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 />
}