Skip to content

Session duration (admin)

A dashboard session expires after a fixed window. This page covers how an org owner adjusts that window for their tenant: where the value lives, who can change it, and how the deviation is recorded.

The member-facing surface (members viewing and revoking their own sessions) lives in Sign-in & access control → Session management.

Upwarden resolves session_duration_minutes per sign-in in two layers:

  1. Platform default. The operator (you, if you run the install) sets STYTCH_SESSION_DURATION_MINUTES in the engine environment. Default: 60 minutes. This is the value every tenant gets when no override is set.
  2. Per-tenant override. An Org-tier or Enterprise-tier owner can override the platform default for their own tenant via the policy-override surface. The override wins for that tenant only; other tenants on the same install keep the platform default.

The resolver lives in src/auth/stytch-login.ts:resolveEffectiveSessionDurationByStytchOrg. It reads the override at sign-in time (no cache) so an owner who just tightened the duration sees the new value on their very next sign-in.

Owner role on a tenant on Org or Enterprise tier. Free and Team tenants are stuck with the platform default; the upgrade path is “go to Org to unlock policy overrides”.

This is the same tier + role gating as every other policy override — see Policy overrides for the full mechanics.

The override accepts a whole number of minutes within:

  • Floor: 5 minutes. Below Stytch’s documented per-session minimum — the SDK would reject anything shorter; Upwarden 400s at the API instead of bubbling a 502 from Stytch.
  • Ceiling: 1440 minutes (24h). Safety bound that sits below the project-level sessions.max_session_duration_minutes cap on the Stytch project. Operators who need longer sessions raise both the constant in src/policy/session-duration-override.tsx and the Stytch project ceiling together.

Anything outside the bounds 400s with a customer-facing reason string (e.g. session_duration_minutes must be at least 5 minutes).

Per the Policy override model:

  • Shorter than the platform default = tighter. The override saves with no typed acknowledgement. Example: dropping from 60 to 30.
  • Longer than the platform default = looser. The override demands a typed acknowledgement_text before the save button enables. The text lands in the audit row so an auditor can verify the operator put their name on the deviation. Example: extending from 60 to 240.

/admin/ui/orgs/<slug>/settings/policies — pick the Session duration (minutes) row, click Edit, enter the new value. If the new value is longer than the platform default, fill in the acknowledgement field.

Terminal window
# Tightening (60 → 30): no acknowledgement required.
curl -fsS -X PUT \
-H "x-upwarden-admin-token: $ADMIN_API_TOKEN" \
-H "content-type: application/json" \
-d '{ "value": 30 }' \
"https://vg-admin.<your-domain>/api/v1/admin/orgs/<slug>/policy-overrides/session_duration_minutes"
# Loosening (60 → 240): acknowledgement_text required.
curl -fsS -X PUT \
-H "x-upwarden-admin-token: $ADMIN_API_TOKEN" \
-H "content-type: application/json" \
-d '{
"value": 240,
"acknowledgement_text": "Approved by CISO — 4h dashboard sessions for incident-response operators only"
}' \
"https://vg-admin.<your-domain>/api/v1/admin/orgs/<slug>/policy-overrides/session_duration_minutes"

DELETE the override to fall back to the platform default:

Terminal window
curl -fsS -X DELETE \
-H "x-upwarden-admin-token: $ADMIN_API_TOKEN" \
"https://vg-admin.<your-domain>/api/v1/admin/orgs/<slug>/policy-overrides/session_duration_minutes"

(Or Reset to default on the dashboard.) The audit row carries event_string = policy_override_restored with policy_name = session_duration_minutes. From the next sign-in onwards the platform default applies again.

Session duration is resolved at sign-in time, not on every request. A change to the override takes effect on the next sign-in for each member:

  • Currently signed-in members keep the session they already have at the old duration. Their session expires whenever it was originally set to expire.
  • The next sign-in (theirs, or a new member’s) picks up the new duration. Magic-link logins, OAuth, passkey, SSO, and TOTP completions all read through the same resolver.

If an owner needs the new duration applied to everyone right now (e.g. tightening in response to an incident), they can force-logout the org via the super-admin surface (Enterprise) or the JSON admin API — every member’s next action is a re-authentication that picks up the new duration. See Sign-in & access control → Session management for the force-logout audit envelopes.

Setting, changing, and clearing the override each write one row into audit_log. The envelopes follow the standard policy-override shape — see Policy overrides → Audit envelopes.

Concretely, for the session-duration policy:

EventCarries
policy_override_tightened{ policy_name: "session_duration_minutes", default_value, override_value, by_user_id }
policy_override_loosened{ policy_name: "session_duration_minutes", default_value, override_value, acknowledgement_text, by_user_id }
policy_override_restored{ policy_name: "session_duration_minutes", by_user_id }

Pivot by policy_name = session_duration_minutes to get a per-tenant history of every duration change. Pivot by by_user_id to attribute the change to a specific operator.