Skip to Content

SDK Next.js (@auris/nextjs)

@auris/nextjs v0.1.0

@auris/nextjs è il pacchetto consigliato per le applicazioni Next.js. Estende @auris/react con utilità server-side che funzionano nei Server Components, Route Handlers e Vercel Edge Middleware.

Il pacchetto espone tre entry point:

Entry pointAmbienteContenuto
@auris/nextjsClient ComponentsRi-esporta tutto @auris/react
@auris/nextjs/serverServer Components, Route HandlersgetSession, withAuth, requirePermission, checkPermission, createServerManagementClient, createServerFgaClient
@auris/nextjs/middlewareEdge MiddlewareaurisMiddleware

Installazione

npm install @auris/nextjs @auris/react @auris/js

Variabili d’Ambiente

Impostale nel file .env.local. Le variabili con prefisso NEXT_PUBLIC_ sono disponibili nei Client Components.

# Lato client (sia browser che server) NEXT_PUBLIC_AURIS_DOMAIN=https://auth.tuodominio.com NEXT_PUBLIC_AURIS_CLIENT_ID=app_xxxxx NEXT_PUBLIC_APP_URL=http://localhost:3000 # Lato server: usate da aurisMiddleware, getSession, withAuth, requirePermission AURIS_DOMAIN=https://auth.tuodominio.com AURIS_CLIENT_ID=app_xxxxx # Solo lato server (mai esposto al browser) AURIS_CLIENT_SECRET=cs_live_xxxxx AURIS_TENANT=my-tenant # Opzionale: abilita la verifica JWT locale senza chiamate di rete AURIS_JWKS_URL=https://auth.tuodominio.com/.well-known/jwks.json

Non includere mai AURIS_CLIENT_SECRET in una variabile con prefisso NEXT_PUBLIC_. Il segreto deve rimanere solo lato server.


Configurazione

Installa il pacchetto e imposta le variabili d’ambiente

Segui i passaggi di installazione e variabili d’ambiente sopra.

Aggiungi AurisProvider al layout root

Avvolgi la tua applicazione in un Client Component che fornisce il contesto Auris a tutti i Client Components figli.

// app/providers.tsx 'use client' import { AurisProvider } from '@auris/nextjs' export function Providers({ children }: { children: React.ReactNode }) { return ( <AurisProvider domain={process.env.NEXT_PUBLIC_AURIS_DOMAIN!} clientId={process.env.NEXT_PUBLIC_AURIS_CLIENT_ID!} redirectUri={`${process.env.NEXT_PUBLIC_APP_URL}/auth/callback`} > {children} </AurisProvider> ) }
// app/layout.tsx import { Providers } from './providers' export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="it"> <body> <Providers>{children}</Providers> </body> </html> ) }

Configura l’Edge Middleware

Proteggi le route prima che raggiungano la logica dell’applicazione.

// middleware.ts (alla radice del progetto, stesso livello di app/) import { aurisMiddleware } from '@auris/nextjs/middleware' export default aurisMiddleware({ domain: process.env.AURIS_DOMAIN!, clientId: process.env.AURIS_CLIENT_ID!, protectedPaths: ['/dashboard/*', '/impostazioni/*'], publicPaths: ['/', '/about', '/prezzi', '/auth/*'], loginUrl: '/auth/login', }) export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico|api/public).*)'], }

Crea le pagine di login e callback

// app/auth/login/page.tsx 'use client' import { useAuris } from '@auris/nextjs' import { useEffect } from 'react' export default function LoginPage() { const { loginWithRedirect } = useAuris() useEffect(() => { loginWithRedirect() }, []) return <p>Reindirizzamento all'accesso in corso...</p> }
// app/auth/callback/page.tsx 'use client' import { useAuris } from '@auris/nextjs' import { useRouter } from 'next/navigation' import { useEffect } from 'react' export default function CallbackPage() { const { isAuthenticated, isLoading, error } = useAuris() const router = useRouter() useEffect(() => { // AurisProvider completa lo scambio del codice automaticamente al mount if (!isLoading && isAuthenticated) router.push('/dashboard') }, [isLoading, isAuthenticated, router]) if (error) return <p>Accesso non riuscito: {error.message}</p> return <p>Completamento accesso in corso...</p> }

AurisProvider rileva i parametri query code e state al mount ed esegue da solo lo scambio del token — la pagina di callback deve solo attendere isAuthenticated e navigare. Se devi attivare lo scambio manualmente (es. fuori dal provider), usa useAuris().client.handleRedirectCallback().


Middleware (@auris/nextjs/middleware)

aurisMiddleware(config)

Protegge le route a livello Edge prima che raggiungano la tua applicazione. Valida il cookie di sessione dell’utente e reindirizza gli utenti non autenticati all’URL di login.

Firma:

import { aurisMiddleware } from '@auris/nextjs/middleware' export default aurisMiddleware(config: AurisMiddlewareConfig)

Configurazione:

OpzioneTipoDefaultDescrizione
domainstringobbligatorioURL base dell’API Auris
clientIdstringobbligatorioClient ID OAuth
tenantstring'default'Nome del tenant/realm
protectedPathsstring[][]Percorsi che richiedono autenticazione. Match esatto, oppure match di prefisso con /* finale (es. '/dashboard/*')
publicPathsstring[][]Percorsi che saltano del tutto i controlli di autenticazione (stesse regole di match)
loginUrlstring'/login'Dove reindirizzare gli utenti non autenticati. Il percorso richiesto in origine viene passato come ?callbackUrl=
callbackPathsstring[]tutti i percorsi protettiPercorsi autorizzati a ricevere il callback OAuth2 (?code=&state=) senza autenticazione

I pattern di percorso non sono espressioni regolari: un pattern corrisponde in modo esatto, oppure come prefisso quando termina con /* ('/dashboard/*' corrisponde a /dashboard e a tutto ciò che sta sotto).

// middleware.ts import { aurisMiddleware } from '@auris/nextjs/middleware' export default aurisMiddleware({ domain: process.env.AURIS_DOMAIN!, clientId: process.env.AURIS_CLIENT_ID!, protectedPaths: [ '/dashboard/*', '/impostazioni/*', '/api/protetto/*', ], publicPaths: [ '/', '/about', '/prezzi', '/blog/*', '/auth/*', '/api/pubblico/*', ], loginUrl: '/auth/login', // Consenti il callback OAuth (?code=&state=) solo su questo percorso callbackPaths: ['/auth/callback'], }) export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'], }

Il modulo esporta anche sanitizeCallbackUrl(url), che restringe un valore callbackUrl a un percorso relativo same-origin. Usalo nella pagina di login prima di reindirizzare al parametro query callbackUrl scritto dal middleware:

import { sanitizeCallbackUrl } from '@auris/nextjs/middleware' const target = sanitizeCallbackUrl(searchParams.get('callbackUrl') ?? '/') router.replace(target)

Helper Server (@auris/nextjs/server)

getSession(config)

Restituisce la sessione dell’utente in un Server Component o Route Handler. Legge il cookie accessToken e lo valida contro l’API Auris (o localmente, senza chiamate di rete, quando jwksUrl è impostato nella config o tramite la variabile d’ambiente AURIS_JWKS_URL).

Firma:

import { getSession } from '@auris/nextjs/server' const session = await getSession({ domain: process.env.AURIS_DOMAIN!, clientId: process.env.AURIS_CLIENT_ID!, }) // Promise<AurisSession | null>

Restituisce:

interface AurisSession { userId: string email: string username?: string firstName?: string lastName?: string roles: string[] tenant?: string accessToken: string }
// app/dashboard/page.tsx — Server Component import { getSession } from '@auris/nextjs/server' import { redirect } from 'next/navigation' export default async function DashboardPage() { const session = await getSession({ domain: process.env.AURIS_DOMAIN!, clientId: process.env.AURIS_CLIENT_ID!, }) if (!session) redirect('/auth/login') return ( <div> <h1>Benvenuto, {session.firstName}</h1> <p>La tua email: {session.email}</p> <pre>{JSON.stringify(session.roles, null, 2)}</pre> </div> ) }

withAuth(handler, config)

Funzione di ordine superiore che avvolge un Route Handler e inietta la sessione autenticata come req.session. Restituisce una risposta 401 se l’utente non è autenticato, e una risposta 403 se il permission opzionale della config non è concesso.

Firma:

import { withAuth } from '@auris/nextjs/server' export const GET = withAuth( async (req: AuthenticatedRequest) => Response, // req.session: AurisSession { domain: string, // obbligatorio clientId: string, // obbligatorio tenant?: string, jwksUrl?: string, permission?: string | string[], // controllo permessi opzionale (403 in caso di fallimento) } )
// app/api/profilo/route.ts import { withAuth } from '@auris/nextjs/server' const aurisConfig = { domain: process.env.AURIS_DOMAIN!, clientId: process.env.AURIS_CLIENT_ID!, } export const GET = withAuth(async (req) => { return Response.json({ id: req.session.userId, email: req.session.email, roles: req.session.roles, }) }, aurisConfig) export const PATCH = withAuth(async (req) => { const body = await req.json() const updated = await aggiornaProfilo(req.session.userId, body) return Response.json(updated) }, aurisConfig)

requirePermission(permission, config, handler)

Come withAuth, ma applica anche un permesso specifico. Restituisce una risposta 403 se l’utente è autenticato ma non ha il permesso richiesto.

Firma:

import { requirePermission } from '@auris/nextjs/server' export const DELETE = requirePermission( 'delete:documents', // string | string[] { domain: string, clientId: string, tenant?: string }, async (req: AuthenticatedRequest) => Response // req.session: AurisSession )
// app/api/documenti/[id]/route.ts import { requirePermission, withAuth } from '@auris/nextjs/server' const aurisConfig = { domain: process.env.AURIS_DOMAIN!, clientId: process.env.AURIS_CLIENT_ID!, } export const GET = withAuth(async (req) => { const id = new URL(req.url).pathname.split('/').at(-1) const doc = await getDocumento(id, req.session.userId) return Response.json(doc) }, aurisConfig) export const DELETE = requirePermission( 'delete:documents', aurisConfig, async (req) => { const id = new URL(req.url).pathname.split('/').at(-1) await eliminaDocumento(id, req.session.userId) return Response.json({ deleted: true }) } )

checkPermission(permission, config)

Restituisce un booleano che indica se l’utente corrente ha un permesso specifico (o tutti i permessi indicati quando si passa un array). Progettato per i Server Components dove hai bisogno di rendering condizionale basato sui permessi.

Firma:

import { checkPermission } from '@auris/nextjs/server' const allowed = await checkPermission('manage:users', { domain: process.env.AURIS_DOMAIN!, clientId: process.env.AURIS_CLIENT_ID!, }) // Promise<boolean>
// app/admin/page.tsx — Server Component import { getSession } from '@auris/nextjs/server' import { checkPermission } from '@auris/nextjs/server' import { redirect } from 'next/navigation' const aurisConfig = { domain: process.env.AURIS_DOMAIN!, clientId: process.env.AURIS_CLIENT_ID!, } export default async function AdminPage() { const session = await getSession(aurisConfig) if (!session) redirect('/auth/login') const puoGestireUtenti = await checkPermission('manage:users', aurisConfig) const puoVedereReport = await checkPermission('view:reports', aurisConfig) return ( <div> <h1>Pannello Admin</h1> {puoGestireUtenti && <GestioneUtenti />} {puoVedereReport && <TabellaReport />} {!puoGestireUtenti && !puoVedereReport && <p>Non hai accesso admin.</p>} </div> ) }

createServerManagementClient(config?)

Crea un client per la Management API usando AURIS_CLIENT_SECRET dalle variabili d’ambiente. Nessuna configurazione richiesta se le variabili d’ambiente sono impostate; ogni campo può essere sovrascritto tramite l’argomento config opzionale.

Firma:

import { createServerManagementClient } from '@auris/nextjs/server' const mgmt = createServerManagementClient()

Il client si autentica con il grant OAuth2 client_credentials usando:

  • AURIS_DOMAIN (fallback su NEXT_PUBLIC_AURIS_URL)
  • AURIS_CLIENT_ID (fallback su NEXT_PUBLIC_AURIS_CLIENT_ID)
  • AURIS_CLIENT_SECRET
  • AURIS_TENANT (default 'default')
// app/api/admin/utenti/route.ts import { createServerManagementClient } from '@auris/nextjs/server' import { requirePermission } from '@auris/nextjs/server' const aurisConfig = { domain: process.env.AURIS_DOMAIN!, clientId: process.env.AURIS_CLIENT_ID!, } export const GET = requirePermission('manage:users', aurisConfig, async () => { const mgmt = createServerManagementClient() const { users } = await mgmt.users.list({ page: 1, limit: 50 }) return Response.json({ users }) })

createServerFgaClient()

Crea un client FGA con un token di accesso M2M. Usalo nei Server Components e Route Handlers per eseguire controlli di autorizzazione Fine-Grained lato server.

Firma:

import { createServerFgaClient } from '@auris/nextjs/server' const fga = await createServerFgaClient()
// app/api/documenti/[id]/route.ts import { withAuth } from '@auris/nextjs/server' import { createServerFgaClient } from '@auris/nextjs/server' export const GET = withAuth(async (req) => { const docId = new URL(req.url).pathname.split('/').at(-1) const fga = await createServerFgaClient() const { allowed } = await fga.check({ objectType: 'document', objectId: docId, relation: 'viewer', subjectType: 'user', subjectId: req.session.userId, }) if (!allowed) { return Response.json({ error: 'Accesso negato' }, { status: 403 }) } const doc = await getDocumento(docId) return Response.json(doc) }, { domain: process.env.AURIS_DOMAIN!, clientId: process.env.AURIS_CLIENT_ID!, })

Client Components

Tutti gli hook e i componenti di @auris/react sono ri-esportati da @auris/nextjs. Nei Client Components, importa da @auris/nextjs invece di @auris/react:

// Nei Client Components Next.js — usa @auris/nextjs, non @auris/react 'use client' import { useAuris, useUser, usePermissions, AuthGuard, PermissionGate } from '@auris/nextjs'

Consulta la pagina SDK React per il riferimento completo di tutti gli hook e componenti.


Esempio di Applicazione Completa

Un’applicazione Next.js minimale ma completa con autenticazione:

app/ layout.tsx — Layout root con AurisProvider page.tsx — Home page pubblica providers.tsx — Wrapper Client Component auth/ login/page.tsx — Avvia il redirect di login callback/page.tsx — Gestisce il redirect da Auris dashboard/ page.tsx — Server Component protetto settings/page.tsx — Protetto con controllo permessi api/ profile/route.ts — Route API protetta admin/ users/route.ts — Route API protetta da permessi middleware.ts — Protezione Edge
// app/providers.tsx 'use client' import { AurisProvider } from '@auris/nextjs' export function Providers({ children }: { children: React.ReactNode }) { return ( <AurisProvider domain={process.env.NEXT_PUBLIC_AURIS_DOMAIN!} clientId={process.env.NEXT_PUBLIC_AURIS_CLIENT_ID!} redirectUri={`${process.env.NEXT_PUBLIC_APP_URL}/auth/callback`} > {children} </AurisProvider> ) }
// app/layout.tsx import { Providers } from './providers' export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="it"> <body> <Providers>{children}</Providers> </body> </html> ) }
// middleware.ts import { aurisMiddleware } from '@auris/nextjs/middleware' export default aurisMiddleware({ domain: process.env.AURIS_DOMAIN!, clientId: process.env.AURIS_CLIENT_ID!, protectedPaths: ['/dashboard/*'], publicPaths: ['/', '/auth/*'], loginUrl: '/auth/login', }) export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'], }
// app/dashboard/page.tsx — Server Component import { getSession } from '@auris/nextjs/server' import { checkPermission } from '@auris/nextjs/server' import { redirect } from 'next/navigation' const aurisConfig = { domain: process.env.AURIS_DOMAIN!, clientId: process.env.AURIS_CLIENT_ID!, } export default async function DashboardPage() { const session = await getSession(aurisConfig) if (!session) redirect('/auth/login') const isAdmin = await checkPermission('manage:users', aurisConfig) return ( <main> <h1>Benvenuto, {session.firstName}</h1> {isAdmin && ( <a href="/dashboard/admin">Vai al Pannello Admin</a> )} </main> ) }
// app/api/profile/route.ts import { withAuth } from '@auris/nextjs/server' const aurisConfig = { domain: process.env.AURIS_DOMAIN!, clientId: process.env.AURIS_CLIENT_ID!, } export const GET = withAuth(async (req) => { return Response.json({ id: req.session.userId, email: req.session.email, firstName: req.session.firstName, roles: req.session.roles, }) }, aurisConfig) export const PATCH = withAuth(async (req) => { const body = await req.json() const updated = await aggiornaProfilo(req.session.userId, body) return Response.json(updated) }, aurisConfig)
// app/api/admin/users/route.ts import { requirePermission, createServerManagementClient } from '@auris/nextjs/server' const aurisConfig = { domain: process.env.AURIS_DOMAIN!, clientId: process.env.AURIS_CLIENT_ID!, } export const GET = requirePermission('manage:users', aurisConfig, async () => { const mgmt = createServerManagementClient() const result = await mgmt.users.list({ page: 1, limit: 50 }) return Response.json(result) })

Correlati