# 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](../manage/scopes-and-claims.md)).

## 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](../getting-started/api-authentication.md).

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

```bash
curl -s -b cookies.txt "$HELIX_URL/admin/realms/$REALM/identity-providers/entra/mappers/role-options"
```
```json
[
  { "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:

```bash
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):

```bash
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

```bash
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](../manage/applications.md), 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`):

```bash
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:

```xml
<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

- [Identity providers](identity-providers.md) — the broker model, JIT provisioning, account linking
- [SAML broker](saml.md) · [OIDC & social brokers](oidc-social.md) · [eHerkenning](eherkenning.md)
