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.

ID Token - Depending on response type is returned from Authentication or from Token endpoint.
{
 kid: "1e9gdk7",
 alg: "RS256"
}.
{
 iss: "http://server.example.com",
 sub: "248289761001",
 aud: "s6BhdRkqt3",
 nonce: "n-0S6_WzA2Mj",
 exp: 1311281970,
 iat: 1311280970,
 name: "Jane Doe",
 given_name: "Jane",
 family_name: "Doe",
 gender: "female",
 birthdate: "0000-10-31",
 email: "janedoe@example.com",
 picture: "http://example.com/janedoe/me.jpg"
}.
[signature]


UserInfo response - Always from UserInfo endpoint.
 {
   "sub": "248289761001",
   "name": "Jane Doe",
   "given_name": "Jane",
   "family_name": "Doe",
   "preferred_username": "j.doe",
   "email": "janedoe@example.com",
   "picture": "http://example.com/janedoe/me.jpg"
  }

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.

    How clients request for claims is handled in more detail in section about attribute filtering



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

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.

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

Exercises

Exercise 4.1

Available contexts in Endpoints

  1. 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>
  2. 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.

    Hints, Tips and Result
    2018-09-20 09:50:48,070 - ERROR [net.shibboleth.idp.profile.impl.ResolveAttributes:314] - Profile Action ResolveAttributes: Error resolving attributes
    net.shibboleth.idp.attribute.resolver.ResolutionException: Attribute Definition 'scriptedAuthenticationFlowId':Script did not run successfully
        at net.shibboleth.idp.attribute.resolver.ad.impl.ScriptedAttributeDefinition$AttributeDefinitionScriptEvaluator.execute(ScriptedAttributeDefinition.java:228)
    Caused by: java.lang.RuntimeException: javax.script.ScriptException: TypeError: null has no such function "getAuthenticationResult" in <eval> at line number 3
        at net.shibboleth.utilities.java.support.scripting.AbstractScriptEvaluator.evaluate(AbstractScriptEvaluator.java:193)
    Caused by: javax.script.ScriptException: TypeError: null has no such function "getAuthenticationResult" in <eval> at line number 3
        at jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:470)
    Caused by: jdk.nashorn.internal.runtime.ECMAException: TypeError: null has no such function "getAuthenticationResult"
    
    
    
  3. 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
  4. Authenticate user using with this new configuration. Notice there are not errors related to resolving the attribute. Can you explain why?

  5. Change the response type back to "code".  Correct the attribute to not cause errors. Correct also the attribute to be carried in tokens.

    Hints, Tips and Result
    <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");
        if (authnCtx != null){
            scriptedAuthenticationFlowId.addValue(authnCtx.getAuthenticationResult().getAuthenticationFlowId());
        }
        ]]></Script>
        <AttributeEncoder xsi:type="oidcext:OIDCString" setToToken="true" name="flow_id"/>
    </AttributeDefinition>
Exercise 4.2

Formatting attribute

  1. Create a new attribute attribute called "manipe" with static values "zero", "1", "3" and "two".

    Hints, Tips and Result
    ...
    
        <AttributeDefinition id="manipe" xsi:type="Simple" sourceAttributeID="manipe">
            <Dependency ref="staticAttributes" />
            <AttributeEncoder xsi:type="oidcext:OIDCString" name="manipe" />
        </AttributeDefinition>
    
    ...
    
       <DataConnector id="staticAttributes" xsi:type="Static">
            <Attribute id="manipe">
                <Value>zero</Value>
                <Value>1</Value>
                <Value>3</Value>
                <Value>two</Value>
            </Attribute>
    ...
  2. Update the filtering rules to release it all RPs asking for profile scope.

    Hints, Tips and Result
    ...
    
        <AttributeFilterPolicy id="OPENID_SCOPE_PROFILE">
            <PolicyRequirementRule xsi:type="oidcext:OIDCScope" value="profile" />
            <AttributeRule attributeID="manipe">
                <PermitValueRule xsi:type="ANY" />
            </AttributeRule>
    ...
  3. Authenticate the user.  Locate "manipe" claim  from the UserInfo response.

    Hints, Tips and Result
    Headers:
        Content-Type:application/json; charset=UTF-8
    Content:{"sub":"VUG4777YP3NMU5KRFESX6SKRAPXLE4MI","zoneinfo":"America\/Los_Angeles","website":"https:\/\/www.facebook.com\/officialtomcruise\/","birthdate":"1962","address":{"street_address":"234 Hollywood Blvd.","country":"US","locality":"Los Angeles","region":"CA","postal_code":"90210"},"email_verified":false,"gender":"male","profile":"https:\/\/fi.wikipedia.org\/wiki\/Tom_Cruise","phone_number_verified":true,"preferred_username":"ttester","locale":"en-US","given_name":"Teppo Matias","middle_name":"Matias","manipe":"zero 1 3 two","picture":"https:\/\/pixabay.com\/fi\/pentu-kissa-kukka-potin-tabby-pentu-2766820\/","updated_at":1509450347,"nickname":"TT","name":"Mr.Teppo Matias Testaaja","phone_number":"+1 (604) 555-1234;ext=5678","family_name":"Testaaja","email":"teppo@example.org"}
  4. Instruct "manipe" to be encoded as an array. Verify the result from the UserInfo response.

    Hints, Tips and Result
    Headers:
        Content-Type:application/json; charset=UTF-8
    Content:{"sub":"VUG4777YP3NMU5KRFESX6SKRAPXLE4MI","zoneinfo":"America\/Los_Angeles","website":"https:\/\/www.facebook.com\/officialtomcruise\/","birthdate":"1962","address":{"street_address":"234 Hollywood Blvd.","country":"US","locality":"Los Angeles","region":"CA","postal_code":"90210"},"email_verified":false,"gender":"male","profile":"https:\/\/fi.wikipedia.org\/wiki\/Tom_Cruise","phone_number_verified":true,"preferred_username":"ttester","locale":"en-US","given_name":"Teppo Matias","middle_name":"Matias","manipe":["zero","1","3","two"],"picture":"https:\/\/pixabay.com\/fi\/pentu-kissa-kukka-potin-tabby-pentu-2766820\/","updated_at":1509450347,"nickname":"TT","name":"Mr.Teppo Matias Testaaja","phone_number":"+1 (604) 555-1234;ext=5678","family_name":"Testaaja","email":"teppo@example.org"}
  5. Instruct "manipe" to be encoded as an array of integers. Verify the result from the UserInfo response.

    Hints, Tips and Result
    Headers:
        Content-Type:application/json; charset=UTF-8
    Content:{"sub":"VUG4777YP3NMU5KRFESX6SKRAPXLE4MI","zoneinfo":"America\/Los_Angeles","website":"https:\/\/www.facebook.com\/officialtomcruise\/","birthdate":"1962","address":{"street_address":"234 Hollywood Blvd.","country":"US","locality":"Los Angeles","region":"CA","postal_code":"90210"},"email_verified":false,"gender":"male","profile":"https:\/\/fi.wikipedia.org\/wiki\/Tom_Cruise","phone_number_verified":true,"preferred_username":"ttester","locale":"en-US","given_name":"Teppo Matias","middle_name":"Matias","manipe":[1,3],"picture":"https:\/\/pixabay.com\/fi\/pentu-kissa-kukka-potin-tabby-pentu-2766820\/","updated_at":1509450347,"nickname":"TT","name":"Mr.Teppo Matias Testaaja","phone_number":"+1 (604) 555-1234;ext=5678","family_name":"Testaaja","email":"teppo@example.org"}
  • No labels