Skip to main content
Version: Next

Browser Sessions

When someone signs in, O2ID sets one o2id_session cookie holding a session container — a group of simultaneously-valid account sessions. One browser can be signed in as several accounts at once; signing in as a second account adds it to the container instead of replacing the first.

The cookie is HttpOnly, SameSite=Lax, scoped to /, and expires when the longest-lived account in the container does. Each account in the container has its own lifetime (see an application's sessionSeconds); an expired account drops out of the container on the next request.

A container is not tenant-scoped, so one browser can hold accounts in several tenants and Organizations at the same time. Each tenant only ever sees its own accounts in the container.

An authorization request with no prompt continues as the container's most-recently-used account for the tenant it resolved to — which is the account that last signed in, or was last picked in the account chooser.

A container holds at most 5 accounts. Signing in beyond that drops the least-recently-used one.

Account chooser

GET /t/{tenant}/select-account lists every account the browser is signed in as, with a "Continue as…" action per account, a "Sign out" action per account, "Use a different account", and "Sign out of all accounts". It's part of the login portal bundle, alongside the login and consent pages.

You don't link to it directly — /oauth2/authorize?prompt=select_account redirects there, and POST /t/{tenant}/select-account (fields: transaction_id, account) completes the pending authorization as the chosen account. The chooser reads the accounts from GET /t/{tenant}/session/accounts, which resolves them from the request's own cookie.

The prompt parameter

prompt on /oauth2/authorize controls whether the user is asked anything before a code is issued.

ValueBehavior
(absent)Continues as the most-recently-used account. Shows the login page if the browser has no account signed in.
loginAlways shows the login page. A successful sign-in adds an account to the container rather than replacing the current one.
select_accountShows the account chooser, even when only one account is signed in. Falls back to the login page when none is.
noneNever shows any UI. Redirects back with error=login_required when no account is signed in, or error=consent_required when the request would need the consent screen.
consentShows the consent screen even for an application the user already consented to. Has no effect on a trusted application, which has no consent step at all.

Any other value is treated as the name of a flow trigger to run on the login page.

max_age, login_hint and id_token_hint are not implemented.

Signing out

POST /t/{tenant}/logout (form-encoded). Resolves what to sign out from the request's own cookie — an account token that isn't in this browser's container signs nothing out.

FieldValue
accountOptional. The account to sign out, from GET /session/accounts. Defaults to this tenant's most-recently-used account.
allOptional. true signs out of every account in the container, across every tenant, and clears the cookie.
redirect_toOptional. Where to send the browser afterwards. Honored only for a relative path; anything else answers 204.

Signing out of one account leaves the container's other accounts signed in. The cookie is cleared once nothing is left in it.

curl -X POST https://your-o2id-instance/t/system/logout \
--cookie "o2id_session=…" \
-d "all=true"

This is the portal's own sign-out, not OIDC RP-Initiated Logout: there is no end_session_endpoint, no id_token_hint validation and no post_logout_redirect_uri allow-list.