Account & single sign-on

Thundera account and single sign-on

One Thundera account signs people in to every product under *.thundera.eu. The auth service at auth.thundera.eu owns identity, sessions, devices, organizations and plans; apps never store passwords.

How it fits together

Piece Where What it does
Sign-in pages https://auth.thundera.eu/login, /register Email + password, social providers, organization SSO (SAML / OIDC)
Session cookies .thundera.eu thundera_token (access, 15 min) and thundera_refresh
Auth API https://auth.thundera.eu/api/v1/auth/* Profile, refresh, devices, apps, billing
OIDC provider https://auth.thundera.eu/.well-known/openid-configuration OAuth 2.1 + PKCE for native apps, MCP clients and resource servers
Account settings https://auth.thundera.eu/settings Profile, security, devices, organization, plans

Web apps on *.thundera.eu

  1. Read the session with GET https://auth.thundera.eu/api/v1/auth/me and credentials: 'include'. It is read-only — it never rotates cookies.
  2. No session (401)? Send the visitor to https://auth.thundera.eu/login?redirect=<your-url>. After sign-in they come back to <your-url>.
  3. On 401 from your own API, call POST /api/v1/auth/refresh once, then retry. If refresh is also 401 the session was revoked: clear local state and go to login.

Using Shell v2? The top bar already reads the session, shows Sign in / Get started and the user menu. await Thundera.ready gives you the user. See the design system.

Cookies are HttpOnly, Secure, SameSite=Lax and scoped to Domain=.thundera.eu, so every Thundera host receives them.

Cookie Trusted device Not trusted
thundera_token 15 minutes 15 minutes
thundera_refresh 400 days 7 days (sliding)

Native and desktop apps

Native apps use the system browser and a registered callback.

  1. Generate a PKCE pair (S256) and open https://auth.thundera.eu/api/v1/auth/native-redirect?callback=<scheme>://<host>&response_type=code&code_challenge=<challenge>&code_challenge_method=S256&state=<random>.
  2. The user confirms Sign in to <App> on this device? (or signs in first).
  3. Your callback receives ?code=…&state=…. Exchange it within 60 seconds: POST /api/v1/auth/native-exchange with {code, code_verifier, callback} → {accessToken, refreshToken, sessionId, trusted}.

Callbacks must exactly match an allow-listed scheme://host[/path]. New apps are added to the allow-list by the portal team. Store tokens in the Keychain (macOS/iOS) or an owner-only file, never in logs or query strings.

OIDC / OAuth 2.1

The auth service is a standards-based authorization server:

  • Discovery: /.well-known/openid-configuration and /.well-known/oauth-authorization-server.
  • Authorization code + PKCE S256 is mandatory; redirect URIs match exactly (loopback http://127.0.0.1/localhost match on any port, per RFC 8252).
  • Dynamic client registration (RFC 7591) at POST /api/v1/oidc/register for MCP clients.
  • Resource indicators (RFC 8707): request resource=<api> to get an access token whose audience is that API (for example https://eam.thundera.eu/mcp).
  • Resource servers can read a caller's plan with GET /api/v1/oidc/entitlements/<app> using the same bearer token.

Access tokens

Access tokens are JWTs issued by https://auth.thundera.eu with audience *.thundera.eu:

{
  "sub": "<user-uuid>",
  "email": "ada@example.com",
  "name": "Ada Lovelace",
  "roles": ["user"],
  "type": "access",
  "sid": "<session-uuid>",
  "gen": 0,
  "trusted": true,
  "exp": 1776768691
}
  • sid ties the token to one device session — revoking that device kills it.
  • gen must be ≥ the user's current session generation; Sign out everywhere bumps it, so every older token is rejected with 401 {"code":"SESSION_REVOKED"}.
  • Verify signatures against the JWKS from discovery; never trust an unverified token.

Devices and sessions

People manage devices at https://auth.thundera.eu/settings. The same is available to apps with the user's access token:

Method Path Purpose
GET /api/v1/auth/devices List the user's sessions across every app (current: true marks this one)
DELETE /api/v1/auth/devices/:id Revoke one device
POST /api/v1/auth/devices/:id/trust · /untrust Toggle trusted (no expiry)
POST /api/v1/auth/devices/revoke-others Sign out every other device
POST /api/v1/auth/logout-everywhere Invalidate every token instantly
POST /api/v1/auth/logout Sign out only this device

Client rules

  • Network errors never sign anyone out — only an explicit 401 from the server does.
  • Never send the refresh token in a URL or log line; POST body or header only.
  • Native clients default to trustDevice: true.
  • Scrub per-user local storage when the signed-in user changes (Shell v2 does this for you).

Organizations and SSO

Companies get an organization with members, roles (owner, admin, member), claimed email domains (matching users join automatically), app access and centralized billing. Owners and admins configure SAML 2.0 or OIDC single sign-on in Settings → Organization → SSO/Identity. Members of an SSO organization sign in through their identity provider; password changes are disabled for them.