docs
    site-kit: Sign in with Sonor
    v7.2.0.md

    @sonordev/site-kit/admin-auth — Sign in with Sonor

    Put a site's own admin area behind a Sonor login. Whoever can open the project in Sonor (its owners and members, the managing agency, platform admins) can sign in; nobody else can. There are no passwords on the site.

    Wiring

    // lib/sonor-sso.ts
    import { createSonorSso } from '@sonordev/site-kit/admin-auth'
    
    export const sso = createSonorSso({
      paths: '/admin',            // or ['/admin', '/staff'] for several areas
      // loginPath: '/admin/login' (default: `${paths[0]}/login`)
      // adminEmails: process.env.ADMIN_EMAILS,
    })
    // proxy.ts
    import { createProxy } from '@sonordev/site-kit/proxy'
    import { sso } from '@/lib/sonor-sso'
    
    export default createProxy({ before: sso.gate, redirects: true, securityHeaders: true })
    // app/api/auth/callback/route.ts
    import { sso } from '@/lib/sonor-sso'
    export const GET = sso.handleCallback
    // app/api/auth/logout/route.ts
    import { sso } from '@/lib/sonor-sso'
    export const GET = sso.handleLogout
    export const POST = sso.handleLogout
    // app/admin/login/page.tsx (server component)
    import { SSO_ERROR_MESSAGES } from '@sonordev/site-kit/admin-auth'
    import { sso } from '@/lib/sonor-sso'
    
    export default async function Login({ searchParams }) {
      const { error, return_to } = await searchParams
      let href: string | null = null
      try { href = await sso.getLoginUrl({ returnTo: return_to }) } catch {}
      return (
        <>
          {error && <p>{SSO_ERROR_MESSAGES[error] ?? error}</p>}
          {href ? <a href={href}>Sign in with Sonor</a> : <p>Sign-in is unavailable.</p>}
        </>
      )
    }
    // Every API route and server action that touches admin data
    const session = await sso.getSession()
    if (!session) return new Response('Unauthorized', { status: 401 })

    JSON endpoints can be guarded in the middleware too: pass apiPaths: ['/api/admin'] and include them in the matcher. They get a 401 JSON response instead of a redirect.

    Env

    Var
    SONOR_API_KEYAlready on every site. Identifies the project and verifies tokens.
    SONOR_SESSION_SECRET32+ random chars (openssl rand -hex 32). Rotate to sign everyone out.
    NEXT_PUBLIC_SITE_URLThe site's public origin, for the callback URL.

    The callback route has to be reachable at callbackPath (default /api/auth/callback) on NEXT_PUBLIC_SITE_URL.

    Rules

    • The gate doesn't cover /api/*. The standard middleware matcher excludes it. Check getSession() in every API route.
    • Don't trust identity from headers, query strings or bodies. getSession() reads the httpOnly cookie and verifies its signature on every call; that's the only source.
    • Moving a site onto this module: pass its existing cookieName and secretEnv so current sessions stay valid.