OAuth Audit
The OAuth services keep authorization codes and refresh tokens as operational state. These rows are short lived and are now written directly to auth_code_t and auth_refresh_token_t instead of being created through the general event store. This avoids high-volume login and refresh-token churn in event_store_t and outbox_message_t.
Audit and login history are recorded separately in append-oriented OAuth audit tables.
Goals
- Show administrators who is currently online.
- Show a user the last login time and session history.
- Track refresh-token rotation and rejected refresh attempts.
- Preserve enough history for support and security review without storing raw secrets.
- Keep the hot login and token-refresh path simple and transactional.
Tables
auth_session_t stores one row per login session. It is the current and historical session summary.
session_ididentifies the browser/device session.login_ts,last_refresh_ts,logout_ts, andexpires_tsdescribe the session lifetime.statusisACTIVE,LOGGED_OUT,EXPIRED, orREVOKED.refresh_countis incremented on each successful refresh-token rotation.ip_address,user_agent, anddevice_idare optional request context fields.
auth_session_audit_t stores append-only auth audit entries.
LOGIN_SUCCEEDEDLOGIN_FAILEDAUTH_CODE_ISSUEDAUTH_CODE_CONSUMEDREFRESH_TOKEN_ISSUEDREFRESH_TOKEN_ROTATEDREFRESH_TOKEN_REJECTEDLOGOUTSESSION_EXPIREDSESSION_REVOKED
auth_refresh_token_t.session_id links the currently valid refresh token to the session that owns it. This removes ambiguity when the same user is logged in from multiple browsers or devices.
Audit rows keep session_id as data, but do not use a hard foreign key to auth_session_t. Audit history must remain groupable by session even if operational session rows are later archived or removed.
Login Flow
When /oauth2/{providerId}/code authenticates the user:
- Insert the authorization code into
auth_code_t. - Insert an
ACTIVEsession intoauth_session_t. - Insert
LOGIN_SUCCEEDEDandAUTH_CODE_ISSUEDaudit rows. - Include the
session_idin the auth code row so the token exchange can attach the refresh token to the same session.
Failed logins write LOGIN_FAILED with the available host, provider, client, request metadata, and failure reason.
Authorization Code Exchange
When grant_type=authorization_code succeeds:
- Delete the consumed auth code from
auth_code_t. - Insert the refresh token into
auth_refresh_token_twith the auth code’ssession_id. - Insert
AUTH_CODE_CONSUMEDandREFRESH_TOKEN_ISSUEDaudit rows.
Refresh Token Rotation
When grant_type=refresh_token succeeds, the service performs one transaction:
- Insert the replacement refresh token.
- Delete the previous refresh token with its expected aggregate version.
- Update
auth_session_t.last_refresh_tsand incrementrefresh_count. - Insert
REFRESH_TOKEN_ROTATEDwith the old and new token ids.
If a refresh token is missing, invalid, or belongs to the wrong client, the service writes REFRESH_TOKEN_REJECTED when enough context is available. Raw refresh-token values must not be stored in audit metadata.
Admin Revocation
Administrators can kick out a user by revoking the user’s current refresh token. Operationally, deleting the refresh token is enough to stop the session from renewing once the current access token expires. The audit/session model adds explicit session state to that behavior.
The revocation operation must run as one transaction:
- Find the refresh token row and its
session_id. - Delete the refresh token from
auth_refresh_token_t. - Update
auth_session_t:status = 'REVOKED'logout_ts = CURRENT_TIMESTAMPend_reason = 'ADMIN_REVOKED'
- Insert
SESSION_REVOKEDintoauth_session_audit_t.
The database patch provides revoke_auth_session_by_refresh_token(host_id, refresh_token, admin_user, reason) for this workflow. Admin screens should call the revoke operation instead of issuing a plain refresh-token delete when the intent is to kick out a logged-in user.
If the refresh token has no session_id, the operation still deletes the token and returns NULL. This preserves backward compatibility with refresh-token rows created before session tracking.
Admin Queries
Current online users:
SELECT *
FROM auth_session_t
WHERE status = 'ACTIVE'
AND (expires_ts IS NULL OR expires_ts > CURRENT_TIMESTAMP);
User login history:
SELECT *
FROM auth_session_t
WHERE host_id = $1
AND user_id = $2
ORDER BY login_ts DESC;
Session duration:
SELECT
login_ts,
COALESCE(logout_ts, last_refresh_ts, CURRENT_TIMESTAMP) - login_ts AS duration
FROM auth_session_t
WHERE host_id = $1
AND session_id = $2;
Retention
auth_session_t can be retained longer than operational token tables. auth_session_audit_t should use a retention policy appropriate for the deployment, for example 90 days or one year. Retention jobs should delete audit rows by event_ts and optionally archive them before deletion.