Skip to Content
SDKsNext.js

Next.js SDK (@auris/nextjs)

@auris/nextjs v0.1.0

@auris/nextjs is the recommended package for Next.js applications. It extends @auris/react with server-side utilities that work in Server Components, Route Handlers, and Vercel Edge Middleware.

The package exposes three entry points:

Entry pointEnvironmentContents
@auris/nextjsClient ComponentsRe-exports all of @auris/react
@auris/nextjs/serverServer Components, Route HandlersgetSession, withAuth, requirePermission, checkPermission, createServerManagementClient, createServerFgaClient
@auris/nextjs/middlewareEdge MiddlewareaurisMiddleware

Installation

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

Environment Variables

Set these in your .env.local file. Variables prefixed with NEXT_PUBLIC_ are available in Client Components.

# Client-side (both browser and server) NEXT_PUBLIC_AURIS_DOMAIN=auth.yourdomain.com NEXT_PUBLIC_AURIS_CLIENT_ID=app_xxxxx NEXT_PUBLIC_APP_URL=http://localhost:3000 # Server-side only (never exposed to the browser) AURIS_CLIENT_SECRET=cs_live_xxxxx AURIS_TENANT=my-tenant # Optional: enables local JWT verification without a network call AURIS_JWKS_URL=https://auth.yourdomain.com/.well-known/jwks.json

Never include AURIS_CLIENT_SECRET in a variable prefixed with NEXT_PUBLIC_. The secret must remain server-side only.


Setup

Install the package and set environment variables

Follow the installation and environment variable steps above.

Add AurisProvider to your root layout

Wrap your application in a Client Component that provides Auris context to all child Client Components.

// 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="en"> <body> <Providers>{children}</Providers> </body> </html> ) }

Configure Edge Middleware

Protect routes before they reach your application logic.

// middleware.ts (at the project root, same level as app/) import { aurisMiddleware } from '@auris/nextjs/middleware' export default aurisMiddleware({ protectedPaths: ['/dashboard(.*)', '/settings(.*)'], publicPaths: ['/', '/about', '/pricing', '/auth/(.*)'], loginUrl: '/auth/login', }) export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico|api/public).*)'], }

Create the login and callback pages

// 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>Redirecting to sign in...</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 { handleRedirectCallback } = useAuris() const router = useRouter() useEffect(() => { handleRedirectCallback().then(() => router.push('/dashboard')) }, []) return <p>Completing sign in...</p> }

Middleware (@auris/nextjs/middleware)

aurisMiddleware(config)

Protects routes at the Edge before they reach your application. Validates the user’s session cookie, and redirects unauthenticated users to the login URL.

Signature:

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

Configuration:

OptionTypeDefaultDescription
protectedPathsstring[][]Path patterns (regex-compatible) that require authentication
publicPathsstring[][]Path patterns that are always accessible without authentication
loginUrlstring'/login'URL to redirect unauthenticated users to
callbackPathstring'/auth/callback'OAuth2 callback path — excluded from protection automatically
onUnauthorized(req) => ResponseCustom handler for unauthenticated requests
// middleware.ts import { aurisMiddleware } from '@auris/nextjs/middleware' export default aurisMiddleware({ protectedPaths: [ '/dashboard(.*)', '/settings(.*)', '/api/protected(.*)', ], publicPaths: [ '/', '/about', '/pricing', '/blog(.*)', '/auth/(.*)', '/api/public(.*)', ], loginUrl: '/auth/login', // Custom response for API routes (return 401 instead of redirect) onUnauthorized: (req) => { if (req.nextUrl.pathname.startsWith('/api/')) { return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401, headers: { 'Content-Type': 'application/json' }, }) } }, }) export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'], }

Server Helpers (@auris/nextjs/server)

getSession()

Returns the user’s session in a Server Component or Route Handler. Reads the session cookie set by the SDK and validates it against the Auris API (or locally if AURIS_JWKS_URL is set).

Signature:

import { getSession } from '@auris/nextjs/server' const session = await getSession(): Promise<Session | null>

Returns:

interface Session { user: UserInfo accessToken: string expiresAt: number // Unix timestamp }
// 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() if (!session) redirect('/auth/login') return ( <div> <h1>Welcome, {session.user.firstName}</h1> <p>Your email: {session.user.email}</p> <pre>{JSON.stringify(session.user.roles, null, 2)}</pre> </div> ) }

withAuth(config, handler)

Higher-order function that wraps a Route Handler and injects the authenticated session. Returns a 401 response if the user is not authenticated.

Signature:

import { withAuth } from '@auris/nextjs/server' export const GET = withAuth( { permission?: string }, async (req: Request, context: { session: Session }) => Response )
// app/api/profile/route.ts import { withAuth } from '@auris/nextjs/server' export const GET = withAuth({}, async (req, { session }) => { return Response.json({ id: session.user.id, email: session.user.email, roles: session.user.roles, }) }) export const PATCH = withAuth({}, async (req, { session }) => { const body = await req.json() const updated = await updateProfile(session.user.id, body) return Response.json(updated) })

requirePermission(permission, config, handler)

Like withAuth, but also enforces a specific permission. Returns a 403 response if the user is authenticated but lacks the required permission.

Signature:

import { requirePermission } from '@auris/nextjs/server' export const DELETE = requirePermission( 'delete:documents', {}, async (req: Request, context: { session: Session }) => Response )
// app/api/documents/[id]/route.ts import { requirePermission, withAuth } from '@auris/nextjs/server' export const GET = withAuth({}, async (req, { session }) => { const id = new URL(req.url).pathname.split('/').at(-1) const doc = await getDocument(id, session.user.id) return Response.json(doc) }) export const DELETE = requirePermission( 'delete:documents', {}, async (req, { session }) => { const id = new URL(req.url).pathname.split('/').at(-1) await deleteDocument(id, session.user.id) return Response.json({ deleted: true }) } )

checkPermission(permission)

Returns a boolean indicating whether the current user has a specific permission. Designed for Server Components where you need conditional rendering based on permissions.

Signature:

import { checkPermission } from '@auris/nextjs/server' const allowed = await checkPermission('manage:users'): 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' export default async function AdminPage() { const session = await getSession() if (!session) redirect('/auth/login') const canManageUsers = await checkPermission('manage:users') const canViewReports = await checkPermission('view:reports') return ( <div> <h1>Admin Panel</h1> {canManageUsers && <UserManagement />} {canViewReports && <ReportsTable />} {!canManageUsers && !canViewReports && <p>You do not have admin access.</p>} </div> ) }

createServerManagementClient()

Creates a Management API client using AURIS_CLIENT_SECRET from environment variables. No configuration required if the environment variables are set.

Signature:

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

The client authenticates with the OAuth2 client_credentials grant using:

  • NEXT_PUBLIC_AURIS_DOMAIN
  • NEXT_PUBLIC_AURIS_CLIENT_ID
  • AURIS_CLIENT_SECRET
  • AURIS_TENANT
// app/api/admin/users/route.ts import { createServerManagementClient } from '@auris/nextjs/server' import { requirePermission } from '@auris/nextjs/server' export const GET = requirePermission('manage:users', {}, async () => { const mgmt = await createServerManagementClient() const { users } = await mgmt.users.list({ page: 1, limit: 50 }) return Response.json({ users }) })

createServerFgaClient()

Creates an FGA client with an M2M access token. Use it in Server Components and Route Handlers to perform Fine-Grained Authorization checks server-side.

Signature:

import { createServerFgaClient } from '@auris/nextjs/server' const fga = await createServerFgaClient()
// app/api/documents/[id]/route.ts import { withAuth } from '@auris/nextjs/server' import { createServerFgaClient } from '@auris/nextjs/server' export const GET = withAuth({}, async (req, { session }) => { 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: session.user.id, }) if (!allowed) { return Response.json({ error: 'Access denied' }, { status: 403 }) } const doc = await getDocument(docId) return Response.json(doc) })

Client Components

All hooks and components from @auris/react are re-exported from @auris/nextjs. In Client Components, import from @auris/nextjs instead of @auris/react:

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

See the React SDK page for the full reference of all hooks and components.


Complete Application Example

A minimal but complete Next.js application with authentication:

app/ layout.tsx — Root layout with AurisProvider page.tsx — Public home page providers.tsx — Client component wrapper auth/ login/page.tsx — Initiates login redirect callback/page.tsx — Handles redirect from Auris dashboard/ page.tsx — Protected Server Component settings/page.tsx — Protected with permission check api/ profile/route.ts — Protected API route admin/ users/route.ts — Permission-protected API route middleware.ts — Edge protection
// 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="en"> <body> <Providers>{children}</Providers> </body> </html> ) }
// middleware.ts import { aurisMiddleware } from '@auris/nextjs/middleware' export default aurisMiddleware({ protectedPaths: ['/dashboard(.*)'], publicPaths: ['/', '/auth/(.*)'], loginUrl: '/auth/login', }) export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'], }
// 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>Redirecting to sign in...</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 { handleRedirectCallback } = useAuris() const router = useRouter() useEffect(() => { handleRedirectCallback().then(() => router.push('/dashboard')) }, []) return <p>Completing sign in...</p> }
// app/dashboard/page.tsx — Server Component import { getSession } from '@auris/nextjs/server' import { checkPermission } from '@auris/nextjs/server' import { redirect } from 'next/navigation' export default async function DashboardPage() { const session = await getSession() if (!session) redirect('/auth/login') const isAdmin = await checkPermission('manage:users') return ( <main> <h1>Welcome, {session.user.firstName}</h1> {isAdmin && ( <a href="/dashboard/admin">Go to Admin Panel</a> )} </main> ) }
// app/api/profile/route.ts import { withAuth } from '@auris/nextjs/server' export const GET = withAuth({}, async (req, { session }) => { return Response.json({ id: session.user.id, email: session.user.email, name: session.user.name, roles: session.user.roles, }) }) export const PATCH = withAuth({}, async (req, { session }) => { const body = await req.json() const updated = await updateProfile(session.user.id, body) return Response.json(updated) })
// app/api/admin/users/route.ts import { requirePermission, createServerManagementClient } from '@auris/nextjs/server' export const GET = requirePermission('manage:users', {}, async () => { const mgmt = await createServerManagementClient() const result = await mgmt.users.list({ page: 1, limit: 50 }) return Response.json(result) })