Skip to main content
This walks through a complete Next.js app: the user signs in with Nemu, your server holds the tokens, and your pages call the management API as that user.
The SDK is server side only, and for one reason: it holds your client_secret. There is no React component, hook or provider to import, and nothing to render. Data reaches the browser as plain props from a server component, which is what the dashboard below does.It has no Node specific imports. Everything cryptographic uses the standard Web Crypto API, so it runs anywhere with globalThis.crypto: Node, Bun, Deno, the Edge runtime and Workers.

Register the application

Open the console, go to Settings, then Applications, and create a Web application.
1

Add the redirect URI

It must match what your app sends, exactly. For local development on port 9999 that is http://localhost:9999/api/auth/callback.
2

Select scopes

This example asks for profile:read, workspaces:read, models:read and usage:read. A token carries only the intersection of what the user approved and what the application holds, so a scope you skip here fails later even though the user consented.
3

Copy the credentials

You get a client_id and a client_secret. The secret is shown once and belongs on your server.

Create the app

Set the environment. SESSION_SECRET encrypts the cookie holding the tokens, so generate a real one and keep it stable.
.env

The OAuth client

Keep one place that builds the client and names the scopes. build_scopes validates as it builds, so a typo throws here instead of turning into a 403 on an unrelated request later.
lib/oauth.ts
authorize_url generates the PKCE pair for you. The verifier and the state have to survive the round trip, so store them in a short lived httpOnly cookie and read them back in the callback.
app/api/auth/login/route.ts

Handle the callback

Compare the returned state against the one you stored before exchanging anything. exchange_code returns a ready client, and nemu.tokens is what you persist.
app/api/auth/callback/route.ts

Refresh in the right place

Refresh tokens rotate, and using one revokes it immediately. If a refresh happens somewhere its result cannot be saved, the old token is already dead and the next request signs the user out with nothing to explain it.
A server component cannot write cookies in Next.js. So the refresh must not happen during a page render. Put it in proxy.ts, which runs before the page and can write to the response.
proxy.ts
Then make it impossible for a stale token to reach the SDK at all. If the token is close to expiry, return nothing and let the page redirect to sign in.
lib/client.ts

Call the API

The page is a server component. It calls the API and hands plain data to the interface, so no token is ever serialized to the browser.
app/dashboard/page.tsx
toJSON() strips the entity’s methods and internal client, leaving a plain object that crosses the server and client boundary cleanly.

Storing the session

The example seals tokens into an httpOnly cookie with AES-256-GCM, using Web Crypto and a key derived with PBKDF2 from SESSION_SECRET. It keeps the whole thing in one file with no database, which is what makes it easy to read. A realistic token pair seals to roughly 800 bytes.
A cookie is a reasonable default and a poor ceiling. It caps at roughly 4KB, it travels on every request, and revoking one session means waiting for it to expire. Move to a server side session store once you have real users.
Whatever you store it in, the rules do not change. Tokens and the client_secret stay on the server, cookies are httpOnly and secure in production, and sameSite is lax so the cookie survives the redirect back from consent.

Run it

Open http://localhost:9999, sign in, and the dashboard shows your account and workspaces read straight from the API.

The full example

The complete app is in the repository under others/examples/oauth-sdk-nextjs, including the sealed cookie implementation, the error states on the sign in screen, and the design tokens.

Prefer Auth.js

If your app already uses Auth.js, or you want the session handling solved for you, there is a second example that wires Nemu in as a custom Auth.js provider instead of doing the flow by hand.

All examples

Both example apps, and which one to start from.

Scopes

Every scope, the aliases, and the rest of what build_scopes accepts.