RushLabs ID developer docs

RushLabs ID is the single sign-on service for RushLabs apps. It is a standard OAuth 2.0 + OpenID Connect provider: your app redirects the user here, we handle email/password + mandatory two-factor authentication, and you get back signed tokens describing who they are.

Endpoints

EndpointPurpose
GET /.well-known/openid-configurationOIDC discovery document
GET /.well-known/jwks.jsonPublic keys (RS256) for verifying tokens
GET /oauth/authorizeStart the authorization-code flow (PKCE S256)
POST /oauth/tokenExchange a code or refresh token for tokens
GET /oauth/userinfoProfile claims for a Bearer access token
POST /oauth/revokeRevoke a refresh token

Scopes

ScopeGrants
openidAn ID token with the user's stable sub
profilegiven_name, family_name, name
emailemail
offline_accessA refresh token

1 · Register your app

Client registration happens on the server (there is no self-serve portal). On the VPS:

cd /root/websites/auth.rushlabs.dev
bun run client:create -- --name "My App" \
  --redirect https://myapp.rushlabs.dev/auth/callback

This prints a client_id and (for confidential server-side apps) a client_secret shown once. Pass --public for SPAs/native apps — they get no secret and must use PKCE (the SSO library always uses PKCE). Pass --first-party to skip the consent screen for RushLabs-owned apps.

2 · Add the flow with @rushlabs/sso

The library lives at packages/rushlabs-sso in the auth repo and has zero dependencies (fetch + WebCrypto). Example with Hono on Bun:

import { Hono } from 'hono';
import { getCookie, setCookie, deleteCookie } from 'hono/cookie';
import { RushLabsSSO } from '@rushlabs/sso';

const sso = new RushLabsSSO({
  clientId: process.env.SSO_CLIENT_ID!,
  clientSecret: process.env.SSO_CLIENT_SECRET, // omit for --public clients
  redirectUri: 'https://myapp.rushlabs.dev/auth/callback',
});

const app = new Hono();

app.get('/auth/login', async (c) => {
  const { url, state, codeVerifier } = await sso.createAuthorization();
  setCookie(c, 'sso_tx', JSON.stringify({ state, codeVerifier }), {
    httpOnly: true, secure: true, sameSite: 'Lax', maxAge: 600, path: '/',
  });
  return c.redirect(url);
});

app.get('/auth/callback', async (c) => {
  const tx = JSON.parse(getCookie(c, 'sso_tx') ?? '{}');
  deleteCookie(c, 'sso_tx', { path: '/' });
  if (!tx.state || c.req.query('state') !== tx.state) return c.text('Bad state', 400);

  const tokens = await sso.exchangeCode(c.req.query('code')!, tx.codeVerifier);
  const user = await sso.verifyToken(tokens.id_token!);
  // user = { sub, email, given_name, family_name, name }
  // Create YOUR app session here (its own cookie), keyed by user.sub.
  return c.redirect('/');
});

3 · Verify tokens on API requests

Access and ID tokens are RS256 JWTs. Verify them locally (no network round-trip after the first JWKS fetch):

const claims = await sso.verifyToken(bearerToken);
// claims.sub is the stable RushLabs user id

Or call await sso.getUserInfo(accessToken) to fetch fresh profile data.

Raw flow (no library)

# 1. Redirect the browser to:
https://auth.rushlabs.dev/oauth/authorize?response_type=code
  &client_id=rl_...&redirect_uri=https://myapp/callback
  &scope=openid+profile+email&state=...&code_challenge=...&code_challenge_method=S256

# 2. Exchange the code:
curl -X POST https://auth.rushlabs.dev/oauth/token \
  -d grant_type=authorization_code -d code=... \
  -d redirect_uri=https://myapp/callback \
  -d client_id=rl_... -d client_secret=rls_... -d code_verifier=...

Accounts & 2FA

Every account uses email + password and mandatory TOTP two-factor authentication — users set it up during registration and confirm a code at each sign-in. Sessions on auth.rushlabs.dev are JWT httpOnly cookies, so returning users who are already signed in go straight through the authorize flow (first-party apps skip consent, too).

Federated providers (Google, X, …)

The provider layer is pluggable. Deployments enable providers via AUTH_PROVIDERS in .env; only local ships enabled today. Adding a provider is a small module implementing IdentityProvider — see docs/adding-providers.md in the repo.