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
- Read the session with
GET https://auth.thundera.eu/api/v1/auth/meandcredentials: 'include'. It is read-only — it never rotates cookies. - No session (401)? Send the visitor to
https://auth.thundera.eu/login?redirect=<your-url>. After sign-in they come back to<your-url>. - On 401 from your own API, call
POST /api/v1/auth/refreshonce, 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.
- Generate a PKCE pair (
S256) and openhttps://auth.thundera.eu/api/v1/auth/native-redirect?callback=<scheme>://<host>&response_type=code&code_challenge=<challenge>&code_challenge_method=S256&state=<random>. - The user confirms Sign in to <App> on this device? (or signs in first).
- Your callback receives
?code=…&state=…. Exchange it within 60 seconds:POST /api/v1/auth/native-exchangewith{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-configurationand/.well-known/oauth-authorization-server. - Authorization code + PKCE S256 is mandatory; redirect URIs match exactly (loopback
http://127.0.0.1/localhostmatch on any port, per RFC 8252). - Dynamic client registration (RFC 7591) at
POST /api/v1/oidc/registerfor MCP clients. - Resource indicators (RFC 8707): request
resource=<api>to get an access token whose audience is that API (for examplehttps://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
}
sidties the token to one device session — revoking that device kills it.genmust be ≥ the user's current session generation; Sign out everywhere bumps it, so every older token is rejected with401 {"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.