Next.js SDK
@webrizen/sso adds "Sign in with Webrizen" to a Next.js App Router app: login, callback,
logout, encrypted sessions and permission checks. It's built on the certified openid-client
library.
Install
The SDK lives in this monorepo at packages/sdk. Inside the monorepo, depend on it with
workspace:*. For separate repositories, publish it to npm (public or private) or install it
from Git.
pnpm add @webrizen/ssoSetup
Register the app
In Webrizen SSO go to Platform → Applications → Register application with the redirect
URI https://your-app.com/api/auth/webrizen/callback. Copy the client ID and secret.
Add environment variables
APP_URL=https://restaurant.webrizen.com
WEBRIZEN_SSO_ISSUER=https://accounts.webrizen.com/api/auth
WEBRIZEN_SSO_CLIENT_ID=…
WEBRIZEN_SSO_CLIENT_SECRET=…
WEBRIZEN_SSO_SESSION_SECRET=… # 32+ random characters, e.g. openssl rand -base64 32Create the client
import { createWebrizenAuth, fromEnv } from "@webrizen/sso/next";
export const webrizen = createWebrizenAuth(fromEnv());Mount the route handler
import { webrizen } from "@/lib/webrizen";
export const GET = webrizen.handler;That one file serves /login, /callback, /logout and /refresh.
Use the session
import { can } from "@webrizen/sso";
import { webrizen } from "@/lib/webrizen";
export default async function Dashboard() {
const session = await webrizen.requireSession("/dashboard");
return (
<>
<h1>Hello {session.user.name}</h1>
<p>{session.org?.name} · {session.org?.roles.join(", ")}</p>
{can(session, "order", "refund") && <RefundButton />}
</>
);
}Routes
| Link | What it does |
|---|---|
/api/auth/webrizen/login | Starts sign-in. Add ?returnTo=/path to come back somewhere specific |
/api/auth/webrizen/login?prompt=select_account | Re-runs sign-in so the user can pick a different organization |
/api/auth/webrizen/login?prompt=create | Opens the sign-up page instead of sign-in |
/api/auth/webrizen/refresh | Uses the refresh token to reload roles and permissions |
/api/auth/webrizen/logout | Signs out of the app and Webrizen SSO |
/api/auth/webrizen/logout?global=false | Signs out of this app only |
API
createWebrizenAuth(config) → {
handler, // route handler for [action]
getSession(): Promise<WebrizenSession | null>,
requireSession(returnTo?): Promise<WebrizenSession>, // redirects to login if signed out
}
can(session, resource, action): boolean
hasRole(session, role): booleantype WebrizenSession = {
user: { id: string; email: string; emailVerified: boolean; name: string; image: string | null };
org: {
id: string; name: string; slug: string; logo: string | null;
roles: string[];
teams: { id: string; name: string }[];
permissions: Record<string, string[]>;
} | null;
accessToken: string;
refreshToken?: string;
idToken?: string;
expiresAt: number; // unix seconds, access token expiry
};How sessions work
- The session is stored in an encrypted, HTTP-only cookie (
A256GCM, key derived fromWEBRIZEN_SSO_SESSION_SECRET). No database is needed in your app. - Default lifetime is 7 days (
sessionMaxAgeto change it). - Roles and permissions are captured at sign-in. Link to
/api/auth/webrizen/refreshafter an admin changes someone's role, or let them sign in again.
Configuration
fromEnv() reads the variables above. For full control, pass a config object:
createWebrizenAuth({
issuer: "https://accounts.webrizen.com/api/auth",
clientId: "…",
clientSecret: "…",
redirectUri: "https://restaurant.webrizen.com/api/auth/webrizen/callback",
postLogoutRedirectUri: "https://restaurant.webrizen.com",
sessionSecret: process.env.SESSION_SECRET!,
sessionMaxAge: 60 * 60 * 12, // 12 hours
scopes: ["openid", "profile", "email", "organization", "offline_access"],
cookieName: "restaurant_session",
});For many protected routes, check for the session cookie in proxy.ts and redirect to
/api/auth/webrizen/login?returnTo=…. Then call requireSession() in pages for the real
check.