Protocol Mapper Monitors

You can use protocol mapper monitors to keep an eye on the configuration of specific protocol mappers. Please be sure that you have read our general introduction to Monitors to understand the context of this feature.

ProtocolMapperWithConfig

This monitor checks the configuration of OIDC protocol mappers. But first, two important disclaimers:

Warning

This monitor will only check protocol mappers that are assigned to a client (either directly or through a scope). If you have a mapper that is not assigned to a client (e.g., because it is assigned to a scope which is not assigned to any clients), this monitor will currently not find it. If you need to be able to detect these, please open an issue and describe your use case, ideally providing a configuration file that we can use to reproduce and test.

Warning

This monitor tests client protocol mappers, not identity provider mappers (which would be assigned to upstream IDP configurations). At the moment, we have not implemented a monitor for the latter. If there is interest in this, we can build it - please open an issue (or contribute it yourself ;).

With that out of the way: This is one of the more situation-specific monitors kcwarden supports. In some environments, protocol mappers are used to add specific information to the access tokens generated by the clients. Using this monitor, you can enforce certain rules about these configurations. To do this, first, you need to check what the configuration you are interested in actually looks like in the JSON config you download from Keycloak. You will find them under the protocolMappers key of the client. One of them may look like this:

{
    "id": "78890c6c-5dfb-4c1c-a469-4f21d170f702",
    "name": "user-id-mapper",
    "protocol": "openid-connect",
    "protocolMapper": "oidc-usermodel-attribute-mapper",
    "consentRequired": false,
    "config": {
        "userinfo.token.claim": "true",
        "user.attribute": "user_id",
        "id.token.claim": "true",
        "access.token.claim": "true",
        "claim.name": "user_id",
        "jsonType.label": "String"
    }
}

This is a mapper that takes the user_id field from the user attributes and writes it to the access token in the user_id key (specified by the claim.name key of the configuration). For the sake of demonstration, let's say you want to ensure that only a user attribute mapper can write to that key, and no other mapper type can do so. The resulting monitor would look like this:

monitors:
- monitor: ProtocolMapperWithConfig
  config:
  - protocol-mapper-type: "^(?!oidc-usermodel-attribute-mapper).*"
    matched-config:  # Define which part of the config dict to look for
      claim.name: user_id
    allowed: []
    severity: Critical
    note: The user_id claim should only be writable from a user attribute mapper

To further ensure the correctness of that field in the access tokens, you could also write a second monitor. This second monitor ensures that usermodel-attribute-mappers can only write to the field from the user_id user attribute, and cannot use any other attributes. Add the following right below the last line of the previous configuration:

  - protocol-mapper-type: "oidc-usermodel-attribute-mapper"
    matched-config:
      claim.name: user_id
      user.attribute: (?!user_id).*
    allowed: []
    severity: Critical
    note: The user_id claim should only be writable from the user_id field of the user attributes

As seen in the two examples, both the protocol-mapper-type and any value below matched-config support regular expressions, so you can use negative lookahead to check for violations of your rules. You can perform matching on arbitrary keys in the config part of the JSON object. If a key specified in matched-config is not found, the entire protocol mapper is treated as not matching—however, since the config keys should be consistent across different instances of the same protocol mapper type, this should rarely be an issue in practice.

Info

Right now, you cannot match based on the name, protocol and consentRequired fields of the config. If you have a use case for this, please open an issue on the repository and we'll add this functionality.