Opening https://<box>/ sends you to the identity provider the box is
configured with. After sign-in you land on Environments, pick one and
open its viewer. Every sign-in, refusal and sign-out is a line in the box’s
audit trail.
Single sign-on
A box authenticates against one OpenID Connect provider: authorization
code flow with PKCE, state and nonce, the ID token verified against
the issuer’s published keys. Two presets exist:
google— Google Workspace. The account chooser is always shown, and the token’shdclaim is checked against the Workspace domains you allow. A personal Google account is refused (“this is not a Google Workspace account”), and so is an account whose email Google reports as unverified.generic— Okta, Azure AD, Keycloak, Auth0 and any other compliant provider. An email the provider marks unverified is refused.
The redirect URI is fixed: https://<box>/_guard/login/callback. Register
exactly that at your provider — a mismatch shows up as
redirect_uri_mismatch at sign-in.
For Google Workspace, your administrator creates the OAuth client and — so that groups can decide roles — a service account with domain-wide delegation over two read-only scopes (groups and user status). unotest walks you through this at onboarding.
Groups → roles
Which groups a person is in comes from one of two sources:
- a groups claim in the ID token (Okta, Azure AD, Keycloak, Auth0);
- the Google Directory API, because Google does not put groups in a token. This is also the only source that can offboard someone while their session is open.
Your group → role mapping is deny by default:
- A group that is not mapped grants nothing. Someone who signs in successfully but belongs to no mapped group is refused — not admitted with reduced rights.
- Someone in two mapped groups gets the stronger role.
- Group names match case-insensitively.
- Nested groups are not followed: list the groups people are directly in.
Without any group mapping, every SSO user is readonly.
An email-domain allowlist can be added as an extra check; membership is still what decides.
Re-checks and offboarding
Every 15 minutes (configurable) the box re-asks the directory whether each open session is still entitled:
- Removed from every mapped group, suspended or deleted → the session ends,
recorded as
session.revoked. - Moved to a group with a different role → the live session’s role changes
in either direction, recorded as
role.changed. - Directory unreachable → the session is left alone and re-checked next time. An outage at your provider never signs your company out.
A login is stricter than a re-check: if the directory cannot be reached while someone is signing in, they are refused rather than admitted on trust.
Sessions
Sessions are held on the box, not in the cookie: the cookie carries an opaque signed id, the record lives in the guard. That is what lets an administrator end a session, and what makes a role change or a disable take effect immediately. A session expires after 24 hours by default.
The session store is in memory. After the box restarts, an SSO user is signed in again silently; a break-glass user retypes their password.
Login routes are rate-limited per client address (10 attempts per minute by default). A successful login resets the counter.
Break-glass account
A box may carry one or more local accounts with scrypt-hashed
passwords. They exist for the day the identity provider is unreachable and
are reachable from https://<box>/_guard/login/static; with SSO
configured, /_guard/login goes straight to your provider.
A break-glass account is an administrator by default. A local readonly
account is allowed too — intended for a box without a directory, where it is
the only way to hand someone view-only access; a box with a directory
should grant readonly through group mapping instead.
Removing an account from the box’s configuration ends its sessions at the next re-check.
Seats
Your licence names a number of seats. A seat is held by anyone who has signed in within the last 30 days (configurable) and is not disabled — the count is rolling, so a contractor who signed in once in March stops consuming a seat on their own.
When every seat is taken:
- someone who does not already hold a seat is refused with “This box
has no free seats. Ask your administrator to free one or raise the
licensed seat count.” and the refusal is recorded as
seat.denied; - everyone who already holds a seat keeps working, including on re-login.
The Administration page shows seats <used>/<total> in use. To free a
seat, disable the person — see Administration.
What is recorded
| Event | When |
|---|---|
login.succeeded | A sign-in completed; carries the provider, the role granted and the client address. |
login.refused | Credentials rejected, an account disabled on this box, or the directory refused the person; carries the reason. |
seat.denied | No free seat; carries seats and seatsUsed. |
logout | The person signed out. |
session.revoked | A session ended by a re-check, by an administrator, or because its provider was removed from the configuration. |
role.changed | A live session’s role moved (directory change or administrator override). |
The full list of events is in Administration.