TutuId

Integration guide

Add “Sign in with Tutu” to your product in four steps, using OAuth2 + PKCE.

Four steps to integrate

1

Frontend starts login

Create an instance with createTutuId({ baseUrl, clientId }) from @tutu/id-sdk, and call idSdk.startLogin() on click. The SDK generates a PKCE pair and redirects to the TutuId authorization page.

2

User authorizes on TutuId

On the /oauth/authorize consent screen the user clicks “Allow” (skipped automatically if already authorized). After TutuId checks client_id and the redirect_uri allowlist, it redirects back to your /auth/callback with an authorization code.

3

Callback page gets the code

On /auth/callback, call idSdk.completeCallback() to get { code, codeVerifier }. Hand these two values to your own backend — never exchange the token directly in the browser.

4

Backend exchanges token → creates account

Your backend exchanges code + codeVerifier for an access_token, calls /userinfo to get { sub, email, emailVerified, ... }, maps or creates a local account by sub, then issues your own session.

★ Account-takeover defense (must follow)

Your backend may only link or create a local account by sub (the unique account ID). Only when emailVerified === true may you link an existing local account by email; otherwise create a separate account and leave the email empty.

Why: an attacker could pre-register a Tutu account with a victim’s email — merging by an unverified email would hand over the victim’s local account. This is a CVE-level red line.

SDK API reference

@tutu/id-sdk · pure client-side, zero dependencies (requires Web Crypto + sessionStorage).

createTutuId({ baseUrl, clientId, redirectUri? })

Create an SDK instance. redirectUri defaults to ${location.origin}/auth/callback, adapting to dev/prod.

startLogin(scope?)

Generates a PKCE pair and redirects to the authorization page, starting “Sign in with Tutu”.

trySilentLogin(scope?)

Silent login: if there’s a central session and prior authorization, it exchanges a code seamlessly; otherwise it falls back to signed-out immediately (without interrupting the user).

completeCallback()

Parses the result on the callback page: on success returns { code, codeVerifier }, on failure/cancel returns { error }.

logoutEverywhere(returnTo?)

Logs out the central session, cascading sign-out across every product in the suite, then returns to returnTo.