Langkau ke kandungan
Dokumentasi · Next.js

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/callback

Salin ID klien dan rahsia klien. Rahsia itu ditunjukkan sekali sahaja.

Tambah pemboleh ubah persekitaran anda

.env.local
USHER_ISSUER=https://acme.usherid.com
USHER_CLIENT_ID=your_client_id
USHER_CLIENT_SECRET=your_client_secret
APP_URL=http://localhost:3000

Pasang klien OpenID Connect

Mana-mana klien yang diperakui berfungsi. Panduan ini menggunakan openid-client, yang mengendalikan penemuan, PKCE dan pengesahan token.

Terminal
pnpm add openid-client

Temui pengeluar anda

Penemuan membaca titik akhir dan kunci pengeluar sekali, jadi tiada apa-apa yang dikod keras.

lib/usher.ts
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.

app/auth/sign-in/route.ts
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.

app/auth/callback/route.ts
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.

app/auth/sign-out/route.ts
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 · app/page.tsx (pratonton)
// 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 />
}

Langkah seterusnya