Auth0, Keycloak and generic OIDC
Auth0 has no roles claim. You add one with a Login Action:
exports.onExecutePostLogin = async (event, api) => { const ns = "https://seylo/roles"; api.idToken.setCustomClaim(ns, event.authorization?.roles ?? []);};Then in Seylo:
- Application type: Single Page Application.
- Identifier: your Auth0 domain.
- Role claim: the exact namespaced string —
https://seylo/rolesin the example above. - Discriminator: none, unless you use Auth0 Organizations, in which case set
org_id. One Auth0 tenant can serve many customers, and without it they share an issuer.
Keycloak
Section titled “Keycloak”- Client: public, standard flow, redirect URI = the portal’s origin.
- Preset Keycloak, identifier = the realm URL, e.g.
https://sso.acme.example/realms/acme. Discovery is appended to that path, not substituted, so the realm path is preserved. - The role claim is nested. Realm roles live at
realm_access.roles; client roles atresource_access.<your-client-id>.roles. Both work as dotted paths, and the preset defaults to the realm path. - Include the
rolesscope, or add a dedicated role mapper — otherwise neither claim appears in the token at all.
Generic OIDC
Section titled “Generic OIDC”Supply the discovery URL — or the issuer — and the name of the claim your roles arrive in. Requirements:
- Discovery must be served over https, and the document must declare the issuer it was fetched from. A document served from one host claiming to speak for another is refused deliberately: its keys could otherwise mint tokens for that issuer.
jwks_uriis required and must be https.- Only asymmetric signing algorithms are accepted.
If several of your customer organisations share one issuer, set a discriminator claim and value. Otherwise the second organisation registered cannot be told apart from the first, and Seylo will refuse the registration rather than guess.
Claim shapes that work
Section titled “Claim shapes that work”For a role claim, all of these are understood:
- an array —
["releaser", "approver"] - a space- or comma-delimited string —
"releaser approver" - a single scalar —
"admin"
The discriminator is deliberately not split. A value of
"evil.example acme.example" must not match acme.example.