Skip to content

Your first login

View as Markdown

This tutorial takes one web application from portal setup through its first completed login. At the end, your backend will hold an access token and refresh token for a user who authenticated through Sudomimus.

Not building a web app? Pick the right guide:

  1. Create or join an organization at with.sudomimus.com. The developer self-serve portal is organization-based: applications and sectors live inside an organization, so you need one before you can create an application. Most accounts create their first organization on the spot (the form pre-fills a suggested name); if a teammate already runs one, have them invite you instead. The /applications and /sectors pages redirect to /organizations until you belong to one.

  2. Create your application inside that organization. When you create the application you receive:

    • The applicationAnchor — a stable, lowercase-kebab identifier (e.g. my-app), the public name of your application across the API.
    • The client-auth private key — shown once at creation, used to sign /establish requests. Store it like any production secret.
    • A per-application Session JWKS URL at https://session-api.sudomimus.com/applications/{applicationAnchor}/jwks.json, used to verify access and refresh tokens by kid. The portal’s Signing keys tab manages rotation; application creation does not return a one-off signing PEM.
  3. Add at least one Return Rule of type CALLBACK, listing the hostnames you will redirect users back to. The concrete callbackUrl is supplied per inquiry on /establish; the rule just gates which hostnames are allowed.

  4. Add at least one Authentication Rule (e.g. PASSKEY_USERNAMELESS, PASSKEY_REASONED, or EMAIL_VERIFICATION) and one Realize Rule (e.g. EMAIL with allowedEmails: ["*"] for a public sign-up). Rules are allowlist-only with default-deny — an application with zero rules in any layer cannot be used.

  5. Take the application live. New applications start in DRAFT. Follow Take an application live for the authoritative readiness, activation, disablement, and reactivation contract.

Every authentication round-trip through Connect has three phases, followed by the shared Session API refresh phase:

  1. Establish — your application backend asks Connect to start an authentication session and gets back a session reference (exposureKey + hiddenKey).
  2. Authenticate — your application sends the user to via.sudomimus.com with the exposureKey; the user completes a passkey or email-OTP challenge there.
  3. Redeem — once via.sudomimus.com hands control back via your callback URL (with exposure-key + confirmation-key in the query string), your backend exchanges the three keys at Connect for a signed access token and refresh token.
  4. Refresh — your backend calls Session API to exchange the refresh token for a fresh access token whenever the current one nears expiry.

See the Connect flow for the full request shapes and how Connect, via.sudomimus.com, and your application interact.

The examples below are a framework-neutral backend skeleton. Replace pendingSessions and the response helpers with your framework’s server-side session store and HTTP primitives. Never put the client-auth private key or hiddenKey in browser code.

Terminal window
pnpm add @sudomimus/connect

Set these values only in your backend environment:

SUDOMIMUS_APPLICATION_ANCHOR=your-application
SUDOMIMUS_CLIENT_AUTH_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
SUDOMIMUS_CALLBACK_URL=https://your-app.com/auth/callback

The callback hostname must match the CALLBACK Return Rule you configured in the portal.

import { ConnectClient, RETURN_METHOD } from "@sudomimus/connect";
const applicationAnchor = process.env.SUDOMIMUS_APPLICATION_ANCHOR!;
const client = new ConnectClient({
clientAuth: {
applicationAnchor,
privateKeyPem: process.env.SUDOMIMUS_CLIENT_AUTH_PRIVATE_KEY!,
},
});
const inquiry = await client.establish({
applicationAnchor,
returnMethods: [{
type: RETURN_METHOD.CALLBACK,
payload: { callbackUrl: process.env.SUDOMIMUS_CALLBACK_URL! },
}],
});
await pendingSessions.save(inquiry.exposureKey, inquiry.hiddenKey);
const hostedLogin = new URL("https://via.sudomimus.com/");
hostedLogin.searchParams.set("exposure-key", inquiry.exposureKey);
return redirect(hostedLogin.toString(), 302);

Open this route in a browser. Sudomimus shows one of the authentication methods allowed by your Layer 1 rules. Complete the challenge with a test account.

After authentication, Sudomimus redirects the browser to your callback with exposure-key and confirmation-key query parameters:

const callbackUrl = new URL(request.url);
const exposureKey = callbackUrl.searchParams.get("exposure-key");
const confirmationKey = callbackUrl.searchParams.get("confirmation-key");
if (exposureKey === null || confirmationKey === null) {
throw new Error("Missing Sudomimus callback keys");
}
const hiddenKey = await pendingSessions.take(exposureKey);
const tokens = await client.redeem({
exposureKey,
hiddenKey,
confirmationKey,
});

take should atomically consume the pending server-side value so a callback cannot reuse it. A successful redeem returns accessToken and refreshToken.

Verify tokens.accessToken against the application’s Session JWKS, including its signature, kid, typ, issuer, audience, expiry, and actor shape. Then read the application-visible user identifier from sub. Follow the complete token verification procedure; do not treat decoding alone as verification.

Persist the refresh token in protected server-side storage, then follow Managing sessions to rotate it. Your first-login path is complete when the callback redeems once, verification succeeds, and your application establishes its own local session without exposing either token to logs or URLs.