Skip to content

Mappers: claims, attributes and roles

Turn what an identity provider sends into Helix user attributes and roles, and decide what a SAML application receives back.

Mappers work in both directions:

  • Identity-provider mappers (inbound) read the claims of an OIDC provider's ID token (and userinfo) or the attributes of a SAML or eID assertion, and write user attributes and assign roles.
  • SAML attribute mappers (outbound) decide which attributes the Helix SAML IdP releases to one SAML client, including the user's roles as a multi-valued attribute.

OIDC applications receive roles in realm_access.roles of their tokens as always, and through a client's User roles protocol mapper (see Scopes & claims).

Identity-provider mappers

Types

Type What it does Fields
ATTRIBUTE_IMPORTER Copies a claim or SAML attribute (its first value) into a user attribute. source, matchBy, targetAttribute
HARDCODED_ATTRIBUTE Sets a fixed user attribute for everyone from this provider. targetAttribute, attributeValue
HARDCODED_ROLE Gives everyone from this provider a realm role. roleId
CLAIM_TO_ROLE Gives a role when a claim or attribute has a value. For a list such as groups, any value counts. source, matchBy, matchMode, matchValue, roleId
USERNAME_TEMPLATE The username of a user the sign-in creates, from a template. attributeValue (the template)
  • source is the claim name (OIDC; nested claims with dots, for example realm_access.roles) or the SAML attribute. matchBy is NAME (default) or FRIENDLY_NAME, for SAML attributes known by their FriendlyName (isMemberOf for urn:oid:1.3.6.1.4.1.5923.1.5.1.1).
  • matchMode is EQUALS (default: one of the values is exactly matchValue) or REGEX (one of the values fully matches matchValue). Patterns use RE2 syntax (no backreferences, no lookaround, (?i) flags supported per RE2), and are matched in time linear in the value (RE2J, no backtracking), so no pattern can slow sign-ins down. A pattern is at most 256 characters. Counted repetition {n} and literal braces are not supported in mapper patterns; use +/*. Anything else RE2 cannot compile is refused when you save it (400 with the error on matchValue). A value longer than 256 characters is never matched; that counts as no match and is audited (IDP_MAPPER_MATCH_ABORTED).
  • targetAttribute may not be username, email or another identity key: those are set by the sign-in itself (for SAML, use the provider's emailAttribute setting).
  • A username template uses ${claim.<name>}, ${attribute.<friendlyName>}, ${email}, ${subject} and ${alias}, each optionally | lowercase or | uppercase. The result is lower-cased, at most 255 characters of letters, digits and . _ @ + -, and never shaped like an e-mail address unless it is the user's own verified address. When it would equal the username or e-mail address of an existing user of the realm (in any case), the default username is used instead and IDP_USERNAME_TEMPLATE_COLLISION is audited. A template never links a sign-in to an existing account.

Sync modes

Mode When the mapper applies
IMPORT Once, when the sign-in creates the user (just-in-time provisioning).
FORCE At every sign-in through the provider. Attributes are overwritten. A role the mapper assigned is removed again when the upstream value no longer matches.
INHERIT A mapper's default: the provider's own sync mode, the syncMode key of its config (IMPORT when unset).

Helix records which mapper assigned which role. Removing a role by hand clears that record too. A FORCE mapper only ever removes a role it assigned itself, and only when no other mapper still holds it for the user. A role the user has any other way — assigned by an admin, through a group, the realm's default role — is never removed. When an admin assigns a mapper-assigned role by hand, it becomes the admin's and no mapper removes it. Deleting a mapper leaves the roles it assigned in place.

The mappers setting of a provider's config (a source=target,… list) keeps working as before: it applies when the sign-in creates the user.

Roles that confer admin permissions

A role mapper hands a role to everyone the provider vouches for, so it follows the same rule as assigning a role by hand:

  • Saving, changing or deleting a mapper whose role confers admin permissions needs an admin who holds those permissions (a realm admin for a role conferring realm-admin, such as admin). Otherwise the API answers 403. The realm import is held to the same rule.
  • The permissions are recorded as approved when the mapper is saved. If the role is later given more admin permissions, the mapper stops assigning it: the sign-in goes on without the role and IDP_ROLE_MAPPING_REFUSED is audited. The console marks the mapper Not assigned: needs approval until an admin holding the permissions saves it again.
  • A sign-in whose mappers would make a user an administrator through a link made by matching an e-mail address is refused (PRIVILEGED_LINK_REFUSED, reason mapped-administrator), like every sign-in of an administrator through such a link.
  • Changing a provider's mappers is a change of the provider: it also needs the admin permissions of every administrator who signs in through it.
  • The other way round, whoever controls what a provider trusts decides who its role mappers make an administrator. Changing or deleting a provider that has role mappers — its endpoints, keys, client, trust anchors, trusted lists (also when you edit the list itself), sync mode or enabled flag — needs every admin permission those roles confer, approved or current. Otherwise the API answers 403.
  • A sign-in refused this way changes nothing: no role, no attribute, no new link.

In the console

Open Federation & eIDs → Identity providers, select a provider and open its Mappers tab. Choose the provider's sync mode (saved with Save changes), then Add mapper. A role mapper picks the role from the realm's roles; the form warns when the role confers admin permissions.

Over the API

Mappers live under /admin/realms/{realm}/identity-providers/{alias}/mappers (permission manage-identity-providers). The examples assume $HELIX_URL, $REALM and a signed-in cookies.txt — see Authenticating to the API.

The roles a role mapper can assign, with the admin permissions each confers:

curl -s -b cookies.txt "$HELIX_URL/admin/realms/$REALM/identity-providers/entra/mappers/role-options"
[
  { "roleId": "4f0c…", "name": "admin", "adminPermissions": ["realm-admin"] },
  { "roleId": "9a21…", "name": "developer", "adminPermissions": [] }
]

Example: Entra ID / AD group → role

Entra ID sends group object ids in the groups claim of the ID token (enable Group claims on the app registration). Give everyone in the engineering group the developer role, and take it away when they leave the group:

curl -s -b cookies.txt -c cookies.txt "$HELIX_URL/admin/realms/$REALM/identity-providers/entra/mappers" >/dev/null
CSRF=$(awk '($6=="__Host-XSRF-TOKEN"||$6=="XSRF-TOKEN")&&$7~/^r1\./{t=$7} END{print t}' cookies.txt)

curl -s -b cookies.txt -H "Content-Type: application/json" -H "X-XSRF-TOKEN: $CSRF" \
  -d '{
        "name": "Engineering group to developer",
        "mapperType": "CLAIM_TO_ROLE",
        "syncMode": "FORCE",
        "source": "groups",
        "matchMode": "EQUALS",
        "matchValue": "3f2a8c1e-5b7d-4e0a-9c6f-1d2e3f4a5b6c",
        "roleId": "9a21…"
      }' \
  "$HELIX_URL/admin/realms/$REALM/identity-providers/entra/mappers"

For an Active Directory federation server that sends distinguished names, match them with a regular expression: "matchMode": "REGEX", "matchValue": "CN=Helix-Developers,OU=Groups,.*".

Example: eHerkenning attribute → role

An eHerkenning assertion names the service the representative is authorised for. Give a role to representatives authorised for one service (the attribute name depends on your broker; check a test assertion):

curl -s -b cookies.txt -H "Content-Type: application/json" -H "X-XSRF-TOKEN: $CSRF" \
  -d '{
        "name": "Authorised for permit service",
        "mapperType": "CLAIM_TO_ROLE",
        "syncMode": "FORCE",
        "source": "serviceId",
        "matchValue": "urn:etoegang:DV:00000001234567890000:services:9001",
        "roleId": "b77e…"
      }' \
  "$HELIX_URL/admin/realms/$REALM/identity-providers/eherkenning/mappers"

Example: import an attribute

curl -s -b cookies.txt -H "Content-Type: application/json" -H "X-XSRF-TOKEN: $CSRF" \
  -d '{ "name": "Department", "mapperType": "ATTRIBUTE_IMPORTER", "source": "department", "targetAttribute": "department" }' \
  "$HELIX_URL/admin/realms/$REALM/identity-providers/entra/mappers"

PUT …/mappers/{id} replaces a mapper (same body) and DELETE …/mappers/{id} removes it. Invalid fields answer 400 {message, fieldErrors}.

To have the sign-in also read the provider's userinfo response (some providers send groups there only), set userinfoEndpoint in an OIDC provider's config. Its sub must equal the ID token's, and the ID token's claims win on a name both carry.

SAML attribute mappers

By default the Helix SAML IdP releases mail and, for a SAML client linked to an Application, the Application's claims, each with NameFormat uri. Without attribute mappers a SAML client receives exactly that, as before. Each mapper adds one attribute (a mapper's attribute replaces a default one of the same Name):

Type Releases source
USER_ATTRIBUTE One claim of the user's profile. the claim, for example email or department
ROLE_LIST The user's realm roles, one AttributeValue per role. optional: only roles starting with this prefix
HARDCODED A fixed value. the value

Every mapper sets the SAML attributeName (Name), an optional friendlyName and the nameFormat: uri (default), basic or unspecified.

In the console: Manage → Applications & clients → OIDC/SAML clients, edit the SAML client, Attribute mappers.

Example: a SAML application receiving roles

Release the user's roles as a multi-valued Role attribute with NameFormat basic (permission manage-clients):

curl -s -b cookies.txt -c cookies.txt "$HELIX_URL/admin/realms/$REALM/saml-clients" >/dev/null
CSRF=$(awk '($6=="__Host-XSRF-TOKEN"||$6=="XSRF-TOKEN")&&$7~/^r1\./{t=$7} END{print t}' cookies.txt)

curl -s -b cookies.txt -H "Content-Type: application/json" -H "X-XSRF-TOKEN: $CSRF" \
  -d '{ "name": "Roles", "mapperType": "ROLE_LIST", "attributeName": "Role", "friendlyName": "role", "nameFormat": "basic" }' \
  "$HELIX_URL/admin/realms/$REALM/saml-clients/attribute-mappers?entityId=https%3A%2F%2Fapp.example.com%2Fsaml"

The assertion then carries:

<saml2:Attribute FriendlyName="role" Name="Role" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
  <saml2:AttributeValue xsi:type="xsd:string">developer</saml2:AttributeValue>
  <saml2:AttributeValue xsi:type="xsd:string">auditor</saml2:AttributeValue>
</saml2:Attribute>

List a client's mappers with GET …/saml-clients/attribute-mappers?entityId=…; PUT and DELETE …/saml-clients/attribute-mappers/{id} change and remove one.

Roles in OIDC tokens

Mapper-assigned roles are ordinary realm roles: they appear in realm_access.roles (as <role>_<realm>) and in a client's User roles protocol mapper. To release only some roles in such a claim, set the protocol mapper's source to prefix:<p>; only roles whose name starts with <p> are emitted.

Realm export and import

A realm export carries identityProviderMappers (with the role by name) and samlAttributeMappers. On import they are written after the roles and their admin permissions, through the same rules as the API: a mapper to a role conferring admin permissions the importing admin does not hold is reported as failed and not saved.

See also