OIDC scopes

IAM appliance’s CAS supports two ways of working with scopes: custom scopes and scope-less claims.

Custom scopes

This is a more traditional way of specifying returned data. It is closely tied to the official OIDC protocol specification and is vital in scenarios where explicit user consent is needed, because scope defines an attribute set the user consents to be transferred to the SP.

It is more heavyweight and (re)configuring it also means restarting the CAS.

Scope-less claims

Scope-less claims do not need any configuration in CAS property file and are also defined on per-service level. They also allow for dynamic user profile attribute resolution and better value handling.

It is very handy in (but not limited to) scenarios where you do not have an user store but you still need to supply some predefined set of attributes in the userinfo response, i.e. when using Client Credentials grant.

However, scope-less claims rely on the SP to specify only the openid as requested scope. Therefore an user cannot consent to specific data set to be given to the SP. Also if the SP uses some programming library that enables only explicitly-specified scopes to pass, this configuration becomes unusable.

Configuring custom OIDC scope

Prepare CAS server property snippet with custom scope definition 001-oidc.properties and place it into /data/volumes/cas/cas.properties.d/. We used name cust for our custom scope but you can name if however you want.

Example content of the 001-oidc.properties file.
# IF NEEDED, configure CAS to read additional attributes from the appliance LDAP -> list them in the docker-compose-cas.yml file, not here

# IF NEEDED, list all scopes you want to use with OIDC
cas.authn.oidc.discovery.scopes=openid,profile,email,address,phone,offline_access,cust

# define custom scope 'cust' and list all claims that belong to it
cas.authn.oidc.core.user-defined-scopes.cust=name,family_name,email,some_custom_claim

# IF NEEDED, define custom claims
cas.authn.oidc.discovery.claims=sub,name,preferred_username,family_name,given_name,middle_name,given_name,profile,picture,nickname,website,zoneinfo,locale,updated_at,birthdate,email,email_verified,phone_number,phone_number_verified,address,some_custom_claim

# define claim to attribute mapping
# syntax:
#   cas.authn.oidc.core.claims-map.$claim=$attributeFromCas
cas.authn.oidc.core.claims-map.name=displayName
cas.authn.oidc.core.claims-map.given_name=givenName
cas.authn.oidc.core.claims-map.email=mail
cas.authn.oidc.core.claims-map.family_name=sn
cas.authn.oidc.core.claims-map.phone_number=telephoneNumber

When you are done, restart the CAS.

systemctl restart iam-cas

Configuring scope-less claims

This configuration is done per-service. We assume you have already mapped all necessary attributes through the docker-compose-cas.yml configuration.

For the scope-less claims to work, the service:

  • Must allow (and use) only the openid scope.

  • Must explicitly define an attribute release policy.

    • We recommend using Return Mapped or similar policy and not to use "all-in" policies like Return All. The Return Mapped policy allows you to specify, rename, remap and generate the attributes, so we deem it the most versatile. Play around a bit with the settings until you find the policy that suits you the best.

Example service registration with scope-less claims
{
  "@class" : "org.apereo.cas.services.OidcRegisteredService",
  "clientId": "rlc-oidc-test",
  "clientSecret": "rlc-test",
  "serviceId" : "^https://your.application.url",
  "name": "OIDC-RLC",
  "id": 201,
  "subjectType": "pairwise",
  "scopes" : [ "java.util.HashSet", [ "openid" ]],
  "supportedGrantTypes": [ "java.util.HashSet", [ "AUTHORIZATION_CODE", "REFRESH_TOKEN" ] ],
  "supportedResponseTypes": [ "java.util.HashSet", [ "CODE", "TOKEN" ] ],
  "bypassApprovalPrompt": true,
  "attributeReleasePolicy" : {
    "@class" : "org.apereo.cas.services.ReturnMappedAttributeReleasePolicy",
	  "allowedAttributes" : {
		  "@class" : "java.util.TreeMap",
		  "attributeInCasThatWeWantToRenameForOIDC" : "attribute_in_oidc",
		  "another_attribute" : "another_attribute",
		  "generated_static_attribute_in_oidc" : "groovy { return ['somevalue'] }"
	  }
  }
}