SSO session.
GASomeone who signs in on your hosted pages stays signed in on that browser. The next app that sends them to TillAuth, whether a Sign in with TillAuth client or the hosted pages again, signs them straight in. Nobody types a password twice, and a stolen cookie on its own gets nowhere it shouldn't.
How it works
- The user signs in on the hosted pages with any method the app allows. The page starts an SSO session and keeps its secret in an
HttpOnly,Secure,SameSite=Strictcookie on the hosted address. Page script never sees it. - Later, an app sends the browser back. The hosted page swaps the cookie for a one-use code that lasts 60 seconds, and the browser spends that code itself.
- The new sign-in is checked like any other: the user must still be active and allowed into the app, and session risk scores it from the browser's own network. Under
enforce, an unusual one gets the full sign-in page instead, and a very unusual one is blocked. - The browser gets a session of its own, with its own refresh token. It carries the original sign-in's second factor, so
mfais true only if that sign-in passed one.
How long it lasts
| It ends when | Detail |
|---|---|
| Unused for the idle limit | 168 hours (7 days) by default. Each sign-in through it starts the idle clock again. |
| The absolute limit passes | 720 hours (30 days) by default, counted from when the user actually signed in. Using it, or signing in through it, never extends it. |
| The sign-in behind it ends | Signing out, a password change or reset, ending sessions from the dashboard, CLI or API, a SCIM deactivation, token theft detection and the session-risk policy all end it. |
| The user signs out everywhere | Signing out on the hosted pages ends the SSO session, the sign-in it came from and every session it started. |
A shorter limit you set later applies to SSO sessions already running. Turning SSO sessions off stops them being used at once; turning them back on lets the ones still within their limits work again.
The policy
SSO sessions are on for every app. Change them on the app's Policies page, from a terminal, or through the API. Limits are whole hours from 1 to 2160 (90 days), and the idle limit can't be above the absolute one. Fields you leave out keep their value.
tilldev auth apps update my-app --sso-session on --sso-idle-hours 12 --sso-absolute-hours 168
PATCH https://tilldev.dev/api/auth/admin/apps/<app>
{ "settings": { "sso_session": { "enabled": true, "idle_hours": 12, "absolute_hours": 168 } } }The Policies page also shows how many SSO sessions are live, how many sign-ins came through one in the last 7 days, and how many of those risk sent to a full sign-in.
With Sign in with TillAuth
Silent sign-in. Add prompt=none to the authorization request to check for a session without showing anything. If the user can't be signed in silently, the browser returns to your redirect_uri with one of these errors and your state. none can't be combined with other prompt values.
| error | Meaning |
|---|---|
login_required | No SSO session on this browser, or it ended, or the sign-in is older than max_age. |
interaction_required | The user has to set up a second factor first. |
consent_required | The client needs consent the user hasn’t given yet. |
Freshness still holds. The auth_time claim is when the user actually signed in, not when the SSO session was used, so max_age and prompt=login still send someone with an old sign-in back through the full page.
Logout. The end_session_endpoint is now on the hosted pages, so it can reach the cookie. Send the user there with an id_token_hint and they're signed out without a question; without one, or with a hint for a different user, they're asked first. A hint is accepted for 30 days after it was issued, even once expired. Only an ID token works as a hint, never an access token.
GET https://tilldev.dev/oidc/logout
?id_token_hint=<id_token>
&post_logout_redirect_uri=https://ops.acme.com/goodbye
&state=<state>The old address, https://auth.tilldev.dev/v1/oidc/logout, redirects there, and the discovery document advertises the new one. A post_logout_redirect_uri must be registered on the client.
The endpoints
The hosted pages use four endpoints of the TillAuth API. If you host your own sign-in pages, they let you offer the same thing: keep the secret in an HttpOnly cookie your server sets, and have the browser spend the code itself so the right network is scored.
| Endpoint | Does |
|---|---|
POST /v1/sso/session | Starts one from a current refresh_token; pass the old secret as replaces to end it. Answers the secret and both end times, or 403 sso_disabled. |
POST /v1/sso/grant | Swaps the secret for a one-use code. Answers 401 login_required with a state once the session has ended. |
POST /v1/sso/token | Spends the code from the user's browser and answers a session. 401 with reason: "risk" means show the full sign-in page; 403 signin_blocked means blocked. |
POST /v1/sso/end | Ends it and everything it touched. With expect_sub, a session of a different user is left alone and the answer says mismatch. |
Where it shows
- The audit log records
sso.session.created,sso.session.used,sso.session.refusedandsso.session.ended, andadmin.sso_session_changedfor policy changes. - Sign-ins through it are scored with the method
sso, sosignin.riskandsignin.risk_blockedshow where they came from. sso.session.used,sso.session.refusedandsso.session.endedcan be sent to your webhooks.
What it doesn't do
- It belongs to one browser and one address. The app's tilldev.app address, its custom domain and the Sign in with TillAuth pages each keep their own, and a private window starts with none.
- It stays inside one app's users. Another TillAuth app never sees it.
- Apps that sign users in through your own forms and the SDKs don't start one unless you call the endpoints above.
- A backend that verifies access tokens itself keeps accepting an ended session's tokens until they expire, at most 10 minutes later.
Related: Sign in with TillAuth · Sessions · Session risk.