The two-layer session model
Triangle separates who you are from what you may do, and gives each its own layer:
- The session layer answers "who you are": authentication at the front door — single sign-on, multi-factor, passkeys — producing a short-lived session credential that rotates continuously.
- The Triangle identity layer answers "what you may do": your platform identity with its role, compliance tier, and brand memberships, minted as platform tokens the APIs actually check.
Neither layer is redundant. A stolen session credential without platform grants is useless against the data boundary; platform tokens without the session layer's continuous re-authentication would live too long to revoke cleanly. The layers are also engine-neutral by contract: consumers depend on the interfaces, not on a vendor, so the session engine can be replaced without touching application code.
The token exchange, step by step
Present the session credential
The client holds a session from the front door. It calls the exchange endpoint with that credential and the venture it is entering.
Verify against the issuer
The platform verifies the session credential against the issuing keys (cached briefly, refreshed on rotation) — no credential is trusted on presentation alone.
Resolve the platform identity
The exchange looks up the linked platform account: role, compliance tier, and brand memberships. An identity with no link is refused — the exchange never auto-creates accounts, because provisioning is a verified-webhook event, not a login side effect.
Mint platform tokens
Triangle issues its own short-lived access token (carrying role, tier, and brand context) plus a rotating refresh token. The platform's APIs, SDKs, and row-level database policies all evaluate these claims.
Lifetimes, rotation, and revocation
Access tokens live on the order of minutes, not days; refresh tokens live longer and rotate on every use — a presented old refresh token is no longer valid. Clients refresh proactively just before expiry, so rotation is invisible in normal use. Revocation is server-side: ending a session ends it everywhere, for every surface — no per-app cleanup.
Signing keys are asymmetric and rotate on a schedule and on incident. The previous key remains verifiable for one generation so in-flight tokens fail naturally instead of all at once; the public key set is served at a well-known endpoint so any service can verify tokens without a shared secret.
Client storage discipline
- Access tokens live in memory only — never in browser storage, where any script could lift them.
- Refresh tokens live in hardened cookies — httpOnly, Secure, same-site — so the client application itself never reads them.
- No token in a URL. Tokens travel in headers and cookies, never in query strings that land in logs and browser history.
Why invite tokens get their own keys
Invitations are tokens too, but their failure mode is different: an invite sits in an inbox for days. Invite tokens are signed with a separate key pair precisely so that rotating the main signing key after a compromise does not silently invalidate every pending invitation — and so that revoking the invite class never touches live sessions. Invites carry their intended role and scope, so acceptance lands the person in the right place with the right grants.
Roles, tiers, and the permission check
Permission resolution has two gates, in order: the compliance tier (a vertical clearance level checked first, and also enforced at the database row level), then role-based permissions scoped to the brand context the token carries. Role and scope travel as token claims, so every service — HTTP, event consumer, or database policy — evaluates the same answer. The database half of that enforcement is at database isolation mechanics.
Boundary
This page describes the public token model. Key material, endpoint configuration, rate-limit values, and session internals ship with authenticated developer access.
