User sessions
User sessions facilitate the single sign-on (SSO) functionality of Goiabada. Once a user logs in, a new session starts. If they try to log in again and their session is still good, they don’t need to go through the authentication process again.
Session configuration
Section titled “Session configuration”There are two configurations related to user sessions:
| Property | Description |
|---|---|
| User session idle timeout in seconds | If there is no activity from the user within this timeframe, the session will be terminated. This checks the last_accessed timestamp of the session. |
| User session max lifetime in seconds | The maximum duration a user session can last, irrespective of user activity. This is checked against the started timestamp of the session. |
Session refresh
Section titled “Session refresh”A user session is “bumped” (receives a new last_accessed timestamp) in two situations:
- When a new authorization request completes
- When a refresh token associated with the session is used to request a new access token
Credential changes and live sessions
Section titled “Credential changes and live sessions”When a user’s credentials change, their existing sessions and refresh tokens stop being usable. This applies to:
| Action | Effect |
|---|---|
| A user resets a forgotten password | All of that user’s sessions are terminated and all of their refresh tokens are revoked |
| A user changes their own password | Same, except the session they are doing it from, which is preserved |
| An administrator sets a user’s password | All of that user’s sessions are terminated and all of their refresh tokens are revoked |
| An administrator disables a user | Same. Re-enabling the account does not restore the old sessions |
This covers every kind of grant, including offline refresh tokens whose browser session has already expired, and tokens obtained through the resource owner password credentials flow. An authorization code that was issued but not yet redeemed also stops being redeemable.
Each of these writes a revoked_user_auth_state entry to the audit log, recording which sessions were terminated and which refresh tokens were revoked.
What this does not cover
Section titled “What this does not cover”Access tokens are self-contained and are not checked against a database by the applications that consume them, so a credential change cannot reach one that has already been issued:
- Goiabada’s own endpoints,
/userinfoand the account and admin APIs, reject a token from a superseded session on the very next request. - A third-party resource server validating an access token by signature alone has no way to know, and will keep accepting that token until it expires.
This is inherent to stateless tokens rather than a limitation of the revocation itself. It is the main reason to keep access tokens short-lived: the default expiration is 5 minutes, and that value is the upper bound on how long a revoked user’s access token can still be accepted elsewhere. If you have raised Token expiration substantially, you have widened that window.
Ending a session
Section titled “Ending a session”A session can be ended explicitly: a user ends one from their own sessions page, or an administrator ends one from the user or client pages. That is a security action rather than housekeeping, so it also cuts off what the session authorized.
| What | Effect |
|---|---|
| The session | Deleted. That device has to sign in again |
| Refresh tokens from that session | Revoked, including offline ones that were still working in the background |
| Authorization codes from that session | Revoked, so a code that was issued but not yet redeemed can no longer be exchanged |
This is what separates ending a session from a session merely expiring. An offline refresh token is meant to outlive the browser session it came from, so it keeps working when that session times out. Ending the session revokes it deliberately.
A sign-in that was still in progress cannot slip past this. If someone was partway through signing in to an application and was relying on the session you ended rather than typing a password, that sign-in does not quietly become a replacement session. Goiabada sends them back to the login page instead.
Nor can a sign-in that had already reached the consent screen. Goiabada checks the session once more at the very last step, just before it hands the application its authorization code, so a sign-in left waiting on that screen is sent back to the login page as well rather than collecting a code on a session that is no longer there.
An application renewing quietly in the background with prompt=none hits the same check and is told rather than shown: it gets a login_required error back, never a login page, so it can decide when to ask the person to sign in again. A client that registered itself is the exception: Goiabada withholds its error redirects, so a silent request that does not return is itself the signal to send the person through an ordinary sign-in.
Logging out is not the same thing. It revokes nothing: no refresh token is marked revoked and no authorization code is touched. It can still end the session itself, and a normal refresh token stops working once the session behind it is gone. Offline refresh tokens are not tied to a session and keep working either way. See /auth/logout for which logout requests end the whole session.
Each termination writes two entries to the audit log: terminated_user_session, recording what was revoked, and deleted_user_session, the lifecycle record beside it. Count terminated_user_session if you want to know how many sessions were ended.
What ending a session does not cover
Section titled “What ending a session does not cover”An access token that was already issued for an offline grant keeps working until it expires, even at Goiabada’s own endpoints. Offline access tokens carry no reference to a session, so there is nothing for Goiabada to match against the session you ended. This differs from a credential change, where Goiabada’s own endpoints reject the old token on the very next request.
The bound is the access token’s own expiration, 5 minutes by default. If you have raised Token expiration substantially, you have widened it.
A different user signs in on the same browser
Section titled “A different user signs in on the same browser”Sessions belong to a browser as much as to a person, so a shared or borrowed browser can arrive at the login page still carrying someone else’s session. That happens whenever a client sends prompt=login or id_token_hint, and whenever the session that was there has simply stopped being valid.
When the person who then signs in is a different user, Goiabada treats it as the browser changing hands:
| What | Effect |
|---|---|
| The previous session | Ended, exactly as if it had been ended explicitly: deleted, with its refresh tokens and unredeemed authorization codes revoked |
| The new sign-in | Gets a session of its own. It never reuses the one that was there |
| The previous user’s other devices | Untouched. Only grants that came from this browser’s session are revoked |
The reason for revoking rather than simply deleting is that the browser is now in someone else’s hands, and the grants it was holding were reachable from it. An offline refresh token obtained through that session would otherwise keep working in the background indefinitely.
Nothing is inherited across the handover. The new user’s authentication stands on its own: if the client asks for a second factor, they are asked for it, regardless of how the previous user had authenticated, and the acr and amr claims in their tokens describe only what they proved.
Each handover writes a cross_user_session_replaced entry to the audit log, naming both users and the session that was ended, alongside the terminated_user_session and deleted_user_session entries for the session itself and the started_new_user_session entry for its replacement. It is the entry that distinguishes a browser changing hands from an administrator ending a session.
Grants issued before this behaviour existed
Section titled “Grants issued before this behaviour existed”Earlier versions of Goiabada did not make this comparison, so a handover on a shared browser could leave a sign-in tied to the previous person’s session. If you are upgrading, a few grants of that kind may already be out there.
Goiabada turns them away as they are presented. An authorization code, and any refresh token descended from one, is checked against the session it names, and one naming a session that belongs to somebody else is refused. Access tokens get the same treatment at Goiabada’s own endpoints, but only when they carry a session reference: an offline grant’s access token carries none, and a resource server of your own validating the signature by itself never consults Goiabada’s sessions at all. An ID token presented as an id_token_hint to log out is checked the same way, so one of these cannot end the previous person’s session without asking.
That check needs the previous session to still be there, and sessions do not last: they are deleted once they idle out or reach their maximum lifetime, two hours and twenty-four hours by default. After that there is nothing left to compare against. An offline refresh token can outlive that by a long way, since its maximum lifetime defaults to a year, so one issued before the upgrade can keep working after the evidence has gone.
It is worth being precise about what such a grant carries, because it is narrower than it sounds. Its tokens name the right person, so it is not access to the previous user’s account, and the amr and auth_time claims describe the sign-in that really happened. What is wrong is acr: it could be raised to the level the previous user’s session had reached, higher than anything its holder actually presented, and it goes on claiming that through every refresh.
If your applications make authorization decisions from acr, and shared browsers are a realistic possibility in your deployment, this is what closes one:
| Situation | What to do |
|---|---|
| The session it came from is still there | End that session, from the user page or the user’s own sessions page. That revokes the refresh tokens and unredeemed codes that came from it, offline ones included |
| The session has already gone | Revoke the client’s consent, from the user page or the user’s own consents page. An offline grant is checked against consent every time it is refreshed, so this stops it at the next one |
| Nothing above fits | Set a new password for the user, or disable them. Either stops every grant that user holds, on every device |
Revoking consent is the option that still works once the session has been swept, and it has two edges worth knowing. It covers every grant that user holds for that client rather than only the one you are worried about, so they will be asked to consent again. And an access token already in flight keeps working until it expires, the same gap that ending a session leaves.
Lowering the offline maximum lifetime on Settings → Tokens does not close these. That setting is read when a grant is first issued, and every refresh afterwards carries the deadline the grant was born with, so lowering it changes nothing for grants that already exist. There is no way to revoke a single token on its own either: Goiabada has no token revocation endpoint.
Deployments where every user has their own browser have nothing to do.
Forcing re-authentication
Section titled “Forcing re-authentication”There are two ways to force re-authentication in an authorization request:
Using max_age
Section titled “Using max_age”Include the max_age parameter to define the maximum acceptable time (in seconds) since the session started.
For instance, if you add max_age=120 to the authentication request, the user needs to re-authenticate if their session started more than 120 seconds (2 minutes) ago, regardless of having a valid session or recent activity.
This is useful when the client needs to ensure that the user authenticated within a specific timeframe. Unlike the session idle timeout (which resets with activity), max_age is always measured from when the session was originally created.
Using prompt=login
Section titled “Using prompt=login”Include prompt=login in the authorization request to always force re-authentication, regardless of session age. Unlike max_age, which only triggers re-authentication for stale sessions, prompt=login will require the user to log in again every time.
See Prompt parameter for details on all supported prompt values.
Example
Section titled “Example”GET /auth/authorize? client_id=my-app& redirect_uri=https://my-app.com/callback& response_type=code& scope=openid& max_age=120& code_challenge=...& code_challenge_method=S256