Skip to content
Scalekit Docs

Next.js session middleware

Add hosted login and an encrypted session cookie to the Next.js App Router

Use ScalekitAuthNext from @scalekit-sdk/node/next to add hosted login, an encrypted sk_session cookie, token refresh, and logout to the App Router.

Typical flow: create one auth instance, export login/callback/logout Route Handlers, and wrap a protected handler with withAuth. Use createMiddleware() to fail closed on every other path. For Edge Runtime, pass ScalekitEdgeClient instead of ScalekitClient.

Requires @scalekit-sdk/node 2.12.0 or later.

Register these URLs in the Scalekit Dashboard under Authentication > Redirects before you test:

Dashboard fieldMust match
Redirect URIredirectUri exactly, for example http://localhost:3000/callback
Post Logout Redirect URIAbsolute URL after full logout, for example http://localhost:3000/
Initiate Login URLLogin path, for example http://localhost:3000/login

Store credentials in environment variables. Never hard-code secrets.

.env
SCALEKIT_ENVIRONMENT_URL=https://your-env.scalekit.com
SCALEKIT_CLIENT_ID=skc_...
SCALEKIT_CLIENT_SECRET=...
COOKIE_ENCRYPTION_SECRET= # openssl rand -base64 32
REDIRECT_URI=http://localhost:3000/callback

Keep COOKIE_ENCRYPTION_SECRET identical on every server instance.

Terminal
npm install @scalekit-sdk/node

ScalekitAuthNext requires a client. It does not accept envUrl alone.

lib/auth.ts
import ScalekitClient from '@scalekit-sdk/node';
import { ScalekitAuthNext } from '@scalekit-sdk/node/next';
const scalekit = new ScalekitClient(
process.env.SCALEKIT_ENVIRONMENT_URL!,
process.env.SCALEKIT_CLIENT_ID!,
process.env.SCALEKIT_CLIENT_SECRET!
);
export const auth = new ScalekitAuthNext({
client: scalekit,
redirectUri: process.env.REDIRECT_URI!,
cookieEncryptionSecret: process.env.COOKIE_ENCRYPTION_SECRET!,
});
app/login/route.ts
import { auth } from '../../lib/auth';
export const GET = auth.createLoginHandler();
app/callback/route.ts
import { auth } from '../../lib/auth';
export const GET = auth.createCallbackHandler();
app/logout/route.ts
import { auth } from '../../lib/auth';
export const GET = auth.createLogoutHandler();
app/account/route.ts
import { auth } from '../../lib/auth';
export const GET = auth.withAuth(async (request, { user }) => {
return Response.json({ sub: user?.sub });
});

Open http://localhost:3000/account. A missing session returns 302 to /login, not a JSON 401.

user is access-token claims. sub is always present. email appears only when you add it as a custom access-token claim.

classScalekitAuthNexthttps://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/frameworks/nextjs.ts
#constructor

Creates the App Router session helper. Pass a ScalekitClient or ScalekitEdgeClient.

paramclientScalekitClient | ScalekitEdgeClient

Auth client. Required. ScalekitClient is Node-only; use ScalekitEdgeClient on Edge Runtime.

paramredirectUristring

Exact Redirect URI registered in the dashboard.

paramcookieEncryptionSecretstring

Secret used to encrypt sk_session. Generate with openssl rand -base64 32.

paramcookieNamestring

Session cookie name.

optional, default sk_session
paramloginPathstring

Login route path.

optional, default /login
paramcallbackPathstring

Callback route path. Also excluded from createMiddleware() gating.

optional, default /callback
paramlogoutPathstring

Logout route path. Also excluded from createMiddleware() gating.

optional, default /logout
parampostLoginRedirectstring

Fallback path after login when returnTo is absent.

optional, default /
parampostLogoutRedirectUristring

Where logout lands. Defaults to postLoginRedirect. Register the absolute URL as Post Logout Redirect URI.

optional
paramfullLogoutboolean

When true, logout ends the Scalekit session with id_token_hint.

optional, default true
returnsScalekitAuthNext

Helper used by Route Handlers and middleware.

export const auth = new ScalekitAuthNext({
client: scalekit,
redirectUri: process.env.REDIRECT_URI!,
cookieEncryptionSecret: process.env.COOKIE_ENCRYPTION_SECRET!,
});
classScalekitAuthNexthttps://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/frameworks/nextjs.ts
#createLoginHandler

Returns a GET Route Handler that starts hosted login and sets the CSRF state cookie.

returns(request: NextRequest) => Promise<NextResponse>

Handler to re-export from app/login/route.ts.

export const GET = auth.createLoginHandler();
classScalekitAuthNexthttps://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/frameworks/nextjs.ts
#createCallbackHandler

Returns a GET Route Handler that exchanges the authorization code and sets sk_session.

returns(request: NextRequest) => Promise<NextResponse>

Handler to re-export from app/callback/route.ts.

export const GET = auth.createCallbackHandler();
classScalekitAuthNexthttps://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/frameworks/nextjs.ts
#createLogoutHandler

Returns a GET Route Handler that clears sk_session. With fullLogout: true, also ends the Scalekit session.

returns(request: NextRequest) => Promise<NextResponse>

Handler to re-export from app/logout/route.ts.

export const GET = auth.createLogoutHandler();
classScalekitAuthNexthttps://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/frameworks/nextjs.ts
#asyncwithAuth

Wraps a Route Handler so it runs only with a valid session. Refreshes the cookie about 10 seconds before expiry. Redirects to loginPath when the session is missing.

paramhandler(request, context) => NextResponse

Route Handler. context.user is access-token claims.

returns(request: NextRequest) => Promise<NextResponse>

Wrapped handler. Missing session → 302, never JSON 401.

export const GET = auth.withAuth(async (request, { user }) => {
return Response.json({ sub: user?.sub });
});
classScalekitAuthNexthttps://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/frameworks/nextjs.ts
#createMiddleware

Fail-closed middleware. Every matched path redirects to login unless it is listed in publicRoutes or is loginPath, callbackPath, or logoutPath.

Next.js reads export const config as a static export. This method cannot generate that object.

parampublicRoutesstring[]

Paths that stay public, for example ['/', '/pricing'].

optional
returns(request: NextRequest) => Promise<NextResponse>

Middleware function to export as the default from middleware.ts.

export default auth.createMiddleware({
publicRoutes: ['/', '/pricing'],
});
export const config = {
runtime: 'nodejs',
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};
classScalekitAuthNexthttps://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/frameworks/nextjs.ts
#asyncgetSession

Read-only session lookup for Server Components, Route Handlers, and Server Actions. Does not refresh or write a cookie. Only createMiddleware() and withAuth() write a new cookie.

returns{ user, expiresAt } | null

Access-token claims and expiry, or null. Never includes accessToken or refreshToken.

const session = await auth.getSession();
if (session) {
console.log(session.user.sub, session.expiresAt);
}
classScalekitAuthNexthttps://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/frameworks/nextjs.ts
#asynccurrentUser

Shortcut for getSession() when only claims are needed.

returnsRecord<string, unknown> | undefined

Access-token claims, or undefined when there is no valid session.

const user = await auth.currentUser();
classScalekitEdgeClienthttps://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/edge.ts
#constructor

Fetch + jose client for the auth methods ScalekitAuthNext needs on Edge Runtime. Not a full ScalekitClient. Use Create client for Organizations, Users, and other API clients.

paramenvUrlstring

Scalekit environment URL.

paramclientIdstring

Application client ID.

paramclientSecretstring

Application client secret.

returnsScalekitEdgeClient

Drop-in client for ScalekitAuthNext.

import { ScalekitEdgeClient } from '@scalekit-sdk/node/edge';
import { ScalekitAuthNext } from '@scalekit-sdk/node/next';
const scalekit = new ScalekitEdgeClient(
process.env.SCALEKIT_ENVIRONMENT_URL!,
process.env.SCALEKIT_CLIENT_ID!,
process.env.SCALEKIT_CLIENT_SECRET!
);
export const auth = new ScalekitAuthNext({
client: scalekit,
redirectUri: process.env.REDIRECT_URI!,
cookieEncryptionSecret: process.env.COOKIE_ENCRYPTION_SECRET!,
});
middleware.ts
import { auth } from './lib/auth';
export default auth.createMiddleware({
publicRoutes: ['/', '/pricing'],
});
export const config = {
runtime: 'experimental-edge',
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};