When a user signs in, two things get created: a session (long-lived, server-side state) and a token (short-lived, sent with each API request). Understanding how they relate is the difference between "auth works" and "auth works correctly under load and across edge cases."
Session — long-lived, server-side
Section titled “Session — long-lived, server-side”The session is a server-side record that says "this user is signed in." It carries:
- The user's identity (
sub). - Metadata: device fingerprint, IP, user agent, sign-in timestamp.
- The session's authentication level (more on this below).
- A unique session id.
Sessions are typically tied to a long-lived cookie or a refresh token. A user remains signed in as long as their session is valid; the session can be revoked at any time (by the user, by the tenant admin, by the platform's risk engine).
Sessions are NOT what your API gets to see directly. Your API sees tokens.
Access token — short-lived, signed, stateless
Section titled “Access token — short-lived, signed, stateless”An access token is a short-lived (typically 5-15 minutes) signed JSON blob that your API validates without talking to IntelliAuth. The token carries:
iss— issuer (your tenant URL).sub— subject (the user's id).aud— audience (your API).scope— granted permissions.exp— expiry timestamp.auth_level— the authentication level achieved (AAL1, AAL2, AAL3).sid— session id (so you can correlate with the session for advanced flows).
Your API verifies the signature against the tenant's JWKS, checks aud matches, checks exp is in the future, checks scope includes what the action needs. All locally. No round-trip to IntelliAuth per request.
Refresh token — the link
Section titled “Refresh token — the link”How does a short-lived access token become a long-lived session? Via the refresh token.
When a user signs in, the SDK gets back two tokens: the access token (short-lived) and a refresh token (long-lived). When the access token expires, the SDK uses the refresh token to get a new access token (and, importantly, a new refresh token — rotation). The user stays signed in transparently; your API stays stateless.
First sign-in: → IntelliAuth: I'm Alice, here's my password ← IntelliAuth: access_token_1 (exp 5min) + refresh_token_1
After 5 minutes: → IntelliAuth: here's refresh_token_1, mint me a new access token ← IntelliAuth: access_token_2 (exp 5min) + refresh_token_2 (refresh_token_1 is now revoked)
After another 5 minutes: → IntelliAuth: here's refresh_token_2, mint me a new access token ← IntelliAuth: access_token_3 (exp 5min) + refresh_token_3 ...The SDK handles all of this. useIntelliAuth().getAccessToken() returns a valid access token, rotating transparently if needed.
Why rotation matters
Section titled “Why rotation matters”Old protocols treated refresh tokens as long-lived bearer tokens — if leaked, attacker has indefinite access. Modern protocols (which IntelliAuth follows) rotate the refresh token on every use. Three benefits:
- Detection. If an attacker uses a stolen refresh token, the legitimate user's next refresh fails (the token is already rotated). The platform sees "refresh token reused" and revokes the whole session — locking out both the attacker and the legitimate user, forcing a fresh sign-in.
- Bounded blast radius. Even if a refresh token leaks and gets used immediately, it's only valid until the next legitimate rotation (typically minutes).
- No silent indefinite access. Refresh tokens carry their own expiry (typically days/weeks); they can't be used forever.
See Refresh token rotation for the protocol details.
Token vs session for revocation
Section titled “Token vs session for revocation”When you need to sign a user out (e.g. they clicked Sign Out, or you suspect compromise), you revoke the session — not just the token.
- Revoking the session invalidates the refresh token (so no new access tokens can be minted) AND adds a denylist entry that your API can check (so existing access tokens are also rejected, even though they're cryptographically valid).
- Revoking just the token doesn't work cleanly — access tokens are stateless, so there's no central place to revoke them. The denylist comes from session revocation.
Most apps don't have to think about this; the SDK's logout() calls the right endpoint to revoke the session, and the API's session-validation middleware checks the denylist transparently. But it's worth knowing the layering so you understand why "I called logout but the user can still hit my API for 5 minutes" might be the access-token-not-yet-expired case (rare; the SDK handles this) or a missing denylist check on the API side.
Sessions in summary
Section titled “Sessions in summary”- Session = server-side state, long-lived, revocable.
- Refresh token = the client's handle to the session, rotates on use.
- Access token = short-lived authorisation envelope, stateless, sent with API requests.
Your API code mostly sees access tokens. The SDK handles refresh + session lifecycle.