ବିଷୟବସ୍ତୁକୁ ଯାଆନ୍ତୁ
ଡକ୍ୟୁମେଣ୍ଟେସନ · Next.js

କ୍ୱିକଷ୍ଟାର୍ଟ: Next.js

App Router ସହ ଏକ Next.js ଆପରେ ସାଇନ-ଇନ ଯୋଡ଼ନ୍ତୁ। @usher/nextjs SDK ଆସୁଛି; ଏହା ଆସିବା ପର୍ଯ୍ୟନ୍ତ, ଏହି ଗାଇଡ ମାନକ OpenID Connect ଫ୍ଲୋ ବ୍ୟବହାର କରେ, ଯାହା ଠିକ୍ ସେଇଆ ଯାହା SDK ଆପଣଙ୍କ ପାଇଁ କରିବ।

ଆରମ୍ଭ କରିବା ପୂର୍ବରୁ

  • App Router ବ୍ୟବହାର କରୁଥିବା ଏକ Next.js 15 କିମ୍ବା 16 ଆପ।
  • ଏକ Usher ଏନଭାଇରନମେଣ୍ଟ। Usher Consoleରେ ଆପଣଙ୍କ ପ୍ରୋଜେକ୍ଟ ଖୋଲନ୍ତୁ, ଏକ ଏନଭାଇରନମେଣ୍ଟ ବାଛନ୍ତୁ, ଏବଂ ଏହାର ଇସୁଅର ଲେଖି ରଖନ୍ତୁ।

ଆପଣଙ୍କ ଆପ ପଞ୍ଜୀକରଣ କରନ୍ତୁ

Consoleରେ ଆପ୍ଲିକେସନକୁ ଯାଆନ୍ତୁ ଏବଂ ଏକ ୱେବ ଆପ୍ଲିକେସନ ତିଆରି କରନ୍ତୁ। ଏହି ରିଡାଇରେକ୍ଟ URI ଠିକ୍ ଏହିପରି ଯୋଡ଼ନ୍ତୁ:

http://localhost:3000/auth/callback

କ୍ଲାଏଣ୍ଟ ID ଓ କ୍ଲାଏଣ୍ଟ ସିକ୍ରେଟ କପି କରନ୍ତୁ। ସିକ୍ରେଟ କେବଳ ଥରେ ଦେଖାଯାଏ।

ଆପଣଙ୍କ ଏନଭାଇରନମେଣ୍ଟ ଭେରିଏବଲ ଯୋଡ଼ନ୍ତୁ

.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

ଏକ OpenID Connect କ୍ଲାଏଣ୍ଟ ଇନଷ୍ଟଲ କରନ୍ତୁ

ଯେକୌଣସି ସାର୍ଟିଫାଏଡ କ୍ଲାଏଣ୍ଟ କାମ କରେ। ଏହି ଗାଇଡ openid-client ବ୍ୟବହାର କରେ, ଯାହା ଡିସକଭରି, PKCE ଓ ଟୋକେନ ଯାଞ୍ଚ ସମ୍ଭାଳେ।

ଟର୍ମିନାଲ
pnpm add openid-client

ଆପଣଙ୍କ ଇସୁଅର ଖୋଜନ୍ତୁ

ଡିସକଭରି ଇସୁଅରର ଏଣ୍ଡପଏଣ୍ଟ ଓ କୀ ଥରେ ପଢ଼େ, ତେଣୁ କିଛି ବି ହାର୍ଡ-କୋଡ କରିବାକୁ ପଡ଼େ ନାହିଁ।

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`

ଲୋକଙ୍କୁ ଲବିକୁ ପଠାନ୍ତୁ

ଏକ ରୁଟ ହ୍ୟାଣ୍ଡଲର ଏକ PKCE ଭେରିଫାୟର ଓ ଏକ state ମୂଲ୍ୟ ତିଆରି କରେ, ସେଗୁଡ଼ିକୁ ଅଳ୍ପ ସମୟର ଏକ କୁକିରେ ରଖେ, ଏବଂ ଲବିକୁ ରିଡାଇରେକ୍ଟ କରେ।

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)
}

କଲବ୍ୟାକ ସମ୍ଭାଳନ୍ତୁ

Usher ବ୍ୟକ୍ତିଙ୍କୁ ଏକ କୋଡ ସହ ଫେରାଇ ପଠାଏ। ଏହାକୁ ଟୋକେନ ସହ ବଦଳାନ୍ତୁ; openid-client ଆପଣଙ୍କ ପାଇଁ state, PKCE ଭେରିଫାୟର ଓ ID ଟୋକେନର ସିଗ୍ନେଚର ଯାଞ୍ଚ କରେ।

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('/')
}

ସାଇନ ଆଉଟ

ଆପଣଙ୍କ ଆପରେ ବ୍ୟକ୍ତିଙ୍କ ସେସନ ଶେଷ କରନ୍ତୁ, ତାପରେ ସେମାନଙ୍କୁ Usherର end-session ଏଣ୍ଡପଏଣ୍ଟକୁ ପଠାନ୍ତୁ, ଯାହାଦ୍ୱାରା ଲବି ମଧ୍ୟ ସେମାନଙ୍କୁ ଭୁଲିଯାଏ।

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)
}

SDK ସହ

@usher/nextjs ପ୍ରକାଶିତ ହେଲେ, ସେହି ଆପ ଏହିପରି ଦେଖାଯିବ। ତଳର ପ୍ରିଭ୍ୟୁ ରିଲିଜ ପୂର୍ବରୁ ବଦଳିପାରେ।

app/layout.tsx · app/page.tsx (ପ୍ରିଭ୍ୟୁ)
// 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 />
}

ପରବର୍ତ୍ତୀ ପଦକ୍ଷେପ