Attribute Definitions
Section Topics
- OIDC user attributes possible locations
- OIDC Attribute resolving principle, authorization, token and userinfo endpoints
- OIDC attribute encoders
OIDC user attributes possible locations
User attributes may be returned either in ID Token http://openid.net/specs/openid-connect-core-1_0.html#IDToken or in UserInfo response http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse.
Whether the attribute ends up to the ID Token or UserInfo response depends on authentication request.
- If claim is requested by standard scope parameter, attribute will be set to userinfo response unless response type is "id_token" (i.e. UserInfo endpoint cannot be accessed).
- Client may specifically request claim to be returned either in ID Token or in UserInfo response
Third option is of course some agreement between RP and OP about non standard scope or some out of band agreement.
OIDC Attribute resolving principle, authorization, token and userinfo endpoints.
- Authentication endpoint. Front-channel endpoint responsible of authenticating the user are asking for consent. May return depending on response type ID Token, authorization code or Access Token or any combination of the three items.
- Token endpoint. Back-channel endpoint that is responsible of authenticating the client RP. Swaps authorization code(or Refresh Token) to Access Token, ID Token and to Refresh Token.
Userinfo endpoint. Back-channel endpoint that can be accessed with Access Token returning UserInfo response.
Attribute Resolving principle
Each of the endpoints perform attribute resolving!
Back-channel endpoints do not have all session/context information available!
Attributes that are based on session/context information must be instructed to be carried in tokens (i.e. encoded directly to authorization code and Access Token) unless implicit flow is used.
Attribute resolvers that use session/context information must check the existence for the source information and fail gracefully if checking fails
Resolver example<AttributeDefinition id="password" xsi:type="ScriptedAttribute" dependencyOnly="true" language="nashorn"> <Script><![CDATA[ logger = Java.type("org.slf4j.LoggerFactory").getLogger("net.shibboleth.idp.script.password"); subjectCtx = profileContext.getSubcontext("net.shibboleth.idp.authn.context.SubjectContext"); subject = subjectCtx.getSubjects()[0]; if (!subject.getPrivateCredentials().isEmpty()){ password.addValue(subject.getPrivateCredentials().toArray()[0].getName()); } ]]></Script> </AttributeDefinition>
OIDC attribute encoders
- oidcext:OIDCString, for encoding an IdPAttribute with simple string values as JSON Object.
- oidcext:OIDCScopedString, for encoding an IdPAttribute with scoped string values as JSON Object.
- oidcext:OIDCByte, for encoding an IdPAttribute with binary values as JSON Object.
Encoder formatting options
Default
<AttributeEncoder xsi:type="oidcext:OIDCString" name="affiliation" /> Input: IdPAttribute["member", "staff"] Output: "affiliation": "member staff"
asArray
<AttributeEncoder xsi:type="oidcext:OIDCString" asArray="true" name="affiliation" /> Input: IdPAttribute["member", "staff"] Output: "affiliation":["member","staff"]
asInt
<AttributeEncoder xsi:type="oidcext:OIDCString" asInt="true" name="updated_at" /> Input: IdPAttribute["1536143427"] Output: "updated_at": 1536143427
asBoolean
<AttributeEncoder xsi:type="oidcext:OIDCString" asBoolean="true" name="email_verified" /> Input: IdPAttribute["true"] Output: "email_verified": true
asObject
<AttributeDefinition id="address" xsi:type="ScriptedAttribute"> <Dependency ref="staticAttributes" /> <Script><![CDATA[address.addValue("{\"street_address\":\""+street_address.getValues().get(0) + "\"," +"\"locality\":\""+locality.getValues().get(0) + "\"," +"\"region\":\""+region.getValues().get(0) + "\"," +"\"postal_code\":\""+postal_code.getValues().get(0) + "\"," +"\"country\":\""+country.getValues().get(0) + "\"}");]]></Script> <AttributeEncoder xsi:type="oidcext:OIDCString" asObject="true" name="address" /> </AttributeDefinition> Output: "address":{"street_address":"234 Hollywood Blvd.","country":"US","locality":"Los Angeles","region":"CA","postal_code":"90210"}
Encoder delivery options
placeToIDToken & denyUserinfo
<!-- This demonstrates a claim that is placed always to id token --> <AttributeDefinition id="email_idtoken" xsi:type="Simple" sourceAttributeID="email"> <Dependency ref="email" /> <AttributeEncoder xsi:type="oidcext:OIDCString" placeToIDToken="true" denyUserinfo="true" name="email" /> </AttributeDefinition>
setToToken
Attribute not resolvable in token and UserInfo endpoints must be carried in tokens if necessary.
<AttributeDefinition id="password" xsi:type="ScriptedAttribute" dependencyOnly="true" language="nashorn"> <Script><![CDATA[ logger = Java.type("org.slf4j.LoggerFactory").getLogger("net.shibboleth.idp.script.password"); subjectCtx = profileContext.getSubcontext("net.shibboleth.idp.authn.context.SubjectContext"); <!-- Subject does not have credentials populated in Token and UserInfo endpoints, have to give up in such case --> if (subjectCtx!=null){ subject = subjectCtx.getSubjects()[0]; password.addValue(subject.getPrivateCredentials().toArray()[0].getName()); } ]]></Script> <AttributeEncoder xsi:type="oidcext:OIDCString" setToToken="true" name="password"/> </AttributeDefinition>
Subject Identifier
Locally unique and never reassigned identifier within the Issuer for the End-User, which is intended to be consumed by the Client. It MUST NOT exceed 255 ASCII characters in length. The value is a case sensitive string.
Two types of Subject Identifiers, public and pairwise, http://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes
Different RPs may belong to same pairwise group by sharing the sector_identifier_uri - value
Placed always in ID Token and UserInfo response, whether requested or not.
Subject Identifier Generation
Extension provides default resolver and a configuration for it.
Attribute Filtering
Section Topics
- Requesting Attributes in OIDC
- Filtering attributes for OIDC RPs
Requesting Attributes in OIDC
There are two main mechanisms for RP to ask for attributes,
- Authentication request scope - parameter
- There is a set of standard scope values profile, email, address and phone.
- The claims requested by the standard scope values are expected to be returned from UserInfo endpoint unless response type is "id_token"
- The claims requested by the standard scope values are treated as voluntary
- You may define your own scope values and rules for them. This has to be agreed by both parties of course.
- Authentication request claims - parameter
- You must declare the target for the claim, ID Token or UserInfo response
- You may list the claim as essential claim
- You may list a expected value for the claim
{ "userinfo": { "given_name": {"essential": true}, "nickname": null, "picture": null, "http://example.info/claims/groups": null }, "id_token": { "email": {"essential": true}, "email_verified": {"essential": true}, "sub": {"value": "248289761001"} "auth_time": {"essential": true}, "acr": {"essential": true, "values": ["urn:mace:incommon:iap:silver", "urn:mace:incommon:iap:bronze"]} } }
Filtering attributes for OIDC RPs
It is your job to resolve and filter attributes to match the requests. OIDC extension has reserved the right to control some claims you should not try to resolve or filter.
oidcext:OIDCScope
PolicyRule which returns true if any of the scope values in the authentication request matches a supplied string.
<!-- This demonstrates a rule that releases email claims in response to all oidc authentication requests having scope email. The requester needs to have scope email as a registered scope. --> <AttributeFilterPolicy id="OPENID_SCOPE_EMAIL"> <PolicyRequirementRule xsi:type="oidcext:OIDCScope" value="email" /> <AttributeRule attributeID="email"> <PermitValueRule xsi:type="ANY" /> </AttributeRule> <AttributeRule attributeID="email_verified"> <PermitValueRule xsi:type="ANY" /> </AttributeRule> </AttributeFilterPolicy>
oidcext:AttributeInOIDCRequestedClaims
Matcher which returns attribute values after comparing them to requested claims parameter of oidc authentication request.
<!-- This demonstrates a rule that releases email in id token if specifically asked to be released as essential for id token --> <AttributeFilterPolicy id="REQUESTED_CLAIMS"> <PolicyRequirementRule xsi:type="ANY" /> <AttributeRule attributeID="email_idtoken"> <PermitValueRule xsi:type="oidcext:AttributeInOIDCRequestedClaims" matchOnlyIDToken="true" onlyIfEssential="true" /> </AttributeRule> </AttributeFilterPolicy>
Exercises
Define a new scope "campus". Target is to have scope "campus" that includes a claim "campus_id" to ID Token.
Add "campusId" attribute definition to attribute resolver and add a value for it in staticAttributes data connector. Add a new filtering rules that releases campusId when scope "campus" is requested.
Modify client RP to request for scope "campus".
nano +643 /etc/httpd/conf.d/auth_openidc.conf OIDCScope "openid campus" service httpd restart
Authenticate the user. Verify from the logs that scope "campus" is being requested. Find out from the logs why it is not being released.
Add scope "campus" for the RP as a valid scope value.
Authenticate the user. Verify from the logs that scope "campus" is being requested. Find out from the logs is it being released to ID Token or to UserInfo response.
Modify "campusId" attribute resolver to encode the claim always and only to ID Token.
Authenticate the user. Verify from the logs that scope "campus" is being requested. Verify from the logs it is being released only to ID Token.
Define attribute release rules to release "campusId" attribute to be released if asked to be released for ID Token as essential claim.
1. Make sure "campusId" is not requested anymore by scope.
nano +643 /etc/httpd/conf.d/auth_openidc.conf OIDCScope "openid"
2. Modify RP to ask "campusId" as essential ID Token claim.
nano +417 /etc/httpd/conf.d/auth_openidc.conf OIDCAuthRequestParams claims=%7B%22id_token%22%3A%7B%22campus_id%22%3A+%7B%22essential%22%3A+true%7D%7D%7D service httpd restart
3. Remove or comment the campus scope filter rule that you added in exercise 4.1.1. Add a new filtering rule that will release "campusId" as a claim only if requested to be released as essential ID Token claim
4. Authenticate the user and verify from the logs the attribute is released. At this point you should be able to do it without hints and tips.
Exercises
Available contexts in Endpoints
Add the following attribute definition to attribute-resolver.xml. Resolver changes require restarting Shibboleth IdP
nano /opt/shibboleth-idp/conf/attribute-resolver.xml <AttributeDefinition id="scriptedAuthenticationFlowId" xsi:type="ScriptedAttribute" language="nashorn"> <Script><![CDATA[ logger = Java.type("org.slf4j.LoggerFactory").getLogger("net.shibboleth.idp.script.password"); authnCtx = profileContext.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext"); scriptedAuthenticationFlowId.addValue(authnCtx.getAuthenticationResult().getAuthenticationFlowId()); ]]></Script> <AttributeEncoder xsi:type="oidcext:OIDCString" setToToken="true" name="flow_id"/> </AttributeDefinition>
Authenticate user using with this new configuration. We are not creating release rules for the attribute, yet. See log for any errors related to the attribute.
Change the response type to "id_token". It means we are using only authentication endpoint.
nano +636 /etc/httpd/conf.d/auth_openidc.conf OIDCResponseType "id_token" service httpd restart
Authenticate user using with this new configuration. Notice there are not errors related to resolving the attribute. Can you explain why?
Change the response type back to "code". Correct the attribute to not cause errors. Correct also the attribute to be carried in tokens.
Formatting attribute
Create a new attribute attribute called "manipe" with static values "zero", "1", "3" and "two".
Update the filtering rules to release it all RPs asking for profile scope.
Authenticate the user. Locate "manipe" claim from the UserInfo response.
Instruct "manipe" to be encoded as an array. Verify the result from the UserInfo response.
Instruct "manipe" to be encoded as an array of integers. Verify the result from the UserInfo response.
Configuring Subject claim
Modify the registration data of the RP to list subject of type pairwise.
nano /opt/shibboleth-idp/metadata/oidc-client.json { "scope":"openid info profile email address phone", "redirect_uris":["https://192.168.0.150:8443/protected/redirect_uri"], "client_id":"test_rp", "client_secret":"testSecret1234", "response_types":["id_token","code"], "grant_types":["authorization_code","implicit","refresh_token"], "subject_type":"pairwise" }
Authenticate the user. Verify from landing page or from logs that the value of subject is different from previously used public value.
- Activate the attributes "subject-public" and "subject-pairwise" by modifying the comment markers. Ensure the filtering rules are such that these attributes are not filtered out but the attribute "subject" is filtered out for your RP.
Modify the registration data of the rp to request subject of type public.
nano /opt/shibboleth-idp/metadata/oidc-client.json { "scope":"openid info profile email address phone", "redirect_uris":["https://IP_ADDRESS:8443/protected/redirect_uri"], "client_id":"test_rp", "client_secret":"testSecret1234", "response_types":["id_token","code"], "grant_types":["authorization_code","implicit","refresh_token"], "subject_type":"public" }
Authenticate the user. Verify from landing page or from logs that the value of subject is now this plain text value.