SSO¶
Anvil 1.0.0 ships with the policy layer for SSO and a fully interactive admin UI for group→role mapping, but without a production SAML ACS. This means:
- You can configure the IdP metadata, SP entity ID, ACS URL, and
group→role mapping from
/admin/ssotoday. - The
/api/auth/sso/assertionendpoint that provisions users and issues JWTs is admin-only — it's a smoke test for the mapping logic, not a login primitive. - A real AuthnResponse parser that validates signature, issuer, audience, NotOnOrAfter, and replay is out of scope for 1.0.0 and will land in a later release.
What you can configure today¶
From Admin → SSO:
- Enabled flag (gates the
/auth/sso/assertionendpoint) - IdP metadata URL and entity ID
- SP entity ID (Anvil-side; defaults to
anvil) - SP ACS URL (where the IdP POSTs the AuthnResponse)
- Attribute names for username / display name / email / groups
- Group→role mappings (one row per rule)
- Default role for users with no matching group
All of this is stored as a single JSONB row under
app_settings.sso.
Group→role resolution¶
When a user logs in via SSO (once the ACS is built), Anvil:
- Reads the asserted groups from the
configured
groups_attribute - For each mapping rule whose
groupappears in the asserted groups, collects the rule'srole - Returns the highest-ranked collected role (admin > operator > viewer)
- If no rule matches, returns the configured
default_role
Concurrency¶
Concurrent admin edits to the SSO config are protected by
optimistic locking: GET /api/auth/sso/config returns a
version (ISO timestamp); PUT requires expected_version to
match. A stale PUT returns 409 Conflict rather than silently
clobbering another admin's changes.
Testing the mapping¶
The admin UI has a Test assertion form that accepts a username
and a comma-separated list of groups and calls
/api/auth/sso/assertion (admin-only). The response shows which
role the user would be assigned and issues a JWT. Use this to
verify your mapping rules before real SSO is live.
Warning
Never remove the require_admin dependency from
/api/auth/sso/assertion. Without it, the endpoint trusts
caller-supplied {username, groups} and becomes a straight
auth bypass.