This is not Enterprise SSO (OIDC) or social login. Those make Prelude the OAuth client of an external identity provider (Okta, Google…). Here Prelude is the authorization server, and your app is the client.
How it works
There are two sides to build, and this guide covers both:- The authorization frontend — the login and consent screens Prelude redirects to. Prelude drives the OAuth protocol; your UI authenticates the user and collects consent, then hands the browser back with an authorization
code. This is exactly what the Prelude Dashboard does for MCP clients today. - The client app — the application asking users to sign in. It uses the new
PrldOAuth2Clientin the Web SDK to start the flow and exchange thecodefor a session.
1
Authorize
The client app calls
PrldOAuth2Client.initiate() and redirects the browser to /v1/session/oauth/authorize with PKCE. Prelude persists the request and 302s to your login UI with an ?oauth_req=<oar_…> parameter.2
Login
Your sign-in route reads
oauth_req, authenticates the user with any method your app supports (OTP, password, passkey, social, SAML), then calls continueOAuth2(oauthReq). Prelude returns the consent URL to navigate to.3
Consent
Your consent route fetches the pending request with
getOAuth2Pending(oauthReq) to display the client name and requested scopes, then submits the user’s decision with decideOAuth2(oauthReq, approve). Prelude returns the client redirect_uri — with a one-time code and state on approval, or error=access_denied on denial.4
Token
The browser lands back on the client app’s
redirect_uri. The client calls handleCallback(), which verifies state, exchanges the code at /v1/session/oauth/token with its PKCE code_verifier, and persists the tokens so a PrldSessionClient on the same domain picks up the session.prld:oauth:inherit scope). A session minted through the OAuth flow cannot itself start another OAuth flow — the chain is capped at one level.
Prerequisites
Before you start, make sure you have:- A Prelude account with access to Prelude Auth
- An Application ID (
appID) — see Applications - Your Management API key for backend calls
- A verified custom domain, or your default
${APP_ID}.session.prelude.devhost — the authorization-server endpoints are anchored to this auth domain - A login UI built with the Web SDK that can host the sign-in and consent screens
Configure Prelude as your authorization server
Enable the OAuth server on your app and point it at your login UI with a singlePUT. default_provider_url is the origin Prelude sends the browser to: it 302s to <default_provider_url>/sign-in from /authorize, and to <default_provider_url>/oauth/consent after login.
client_id (an oac_… identifier) the client app needs:
client_name you set here is what the consent screen shows the user. See the OAuth Server Config API reference for the full configuration schema — including DCR and CIMD if you want clients to self-register, as covered in the MCP Login guide.
Build the authorization frontend
Prelude handles the OAuth protocol but hands the browser to your login UI to authenticate the user and collect consent. Build two routes at the origin you set asdefault_provider_url. Both use a standard Web SDK session client:
1
Sign-in route (/sign-in)
Prelude redirects here with an If the user already has a live session when they land here, you can skip straight to
?oauth_req=<oar_…> parameter. Authenticate the user with any method your app supports, then resume the flow with continueOAuth2. Prelude returns the consent URL to navigate to.continueOAuth2.2
Consent route (/oauth/consent)
Prelude redirects here with the same
?oauth_req=. This route requires a signed-in session — if there is none, send the user to /sign-in?oauth_req=<oar_…> first, preserving the parameter. Fetch the pending request to render consent, then submit the decision.The
continue, pending, and decision calls run against the user’s live session and are bound to it with DPoP. The Web SDK manages the session and the DPoP proof for you — you do not construct these requests by hand.Build the client app
The application signing users in acts as a public OAuth client. The Web SDK shipsPrldOAuth2Client for exactly this: initiate() builds the authorize URL (generating and storing the PKCE code_verifier and CSRF state), and handleCallback() validates the returned state, exchanges the code, and persists the session.
1
Start the flow
Call
initiate() and send the browser to the returned authorizationURL. The PKCE verifier and state are persisted for you; initiate() does not navigate, so you do it.initiate() also accepts per-call { scopes, state } if you want to override the constructor defaults or supply your own state.2
Handle the callback
At your
redirectURI, pass the query parameters to handleCallback(). It throws PrldErrors.OAuth2 on a provider error, a state mismatch, or a failed exchange.handleCallback() returns the raw token result ({ accessToken, tokenType, expiresIn, refreshToken?, scope? }) if you want to inspect it, but you rarely need to — it persists the session for you, so continue with a PrldSessionClient as you would after any other login.What’s next?
Web SDK
Build the sign-in and consent screens your login UI hosts.
MCP Login
The same authorization server, for MCP clients — including DCR and CIMD self-registration.
OAuth Server API
The raw authorize, token, continue, pending, and decision endpoints.
Groups & scopes
Control which scopes a session — and its OAuth children — can carry.