OIDC: scoped sub with client_credentials?
Simon Lundström
simlu at su.se
Fri Nov 6 10:25:50 UTC 2026
Hey all,
We've been testing the waters with OIDC using both code flow and
client_credentials (for M2M). I've read Scotts stern warning[1] so I
don't wanna let this go before going to prod.
Our strategy is basically go with using subject-id (i.e.
eduPersonPrincipalName) and use the public subject type as the default.
I have setup an OIDC RP using mod_auth_openidc using the
authorization_code flow which gets sub scoped.
How ever when I setup an OAuth2 Resource Server (aka API) and tried to
authenticate a client using the client_credentials grant sub doesn't get
scoped.
I've used the provided examples and pasted in briefs below of what I
think is relevant:
RP with authorization_code grant which works:
<oidcmd:OAuthRPExtensions
grant_types="authorization_code"
response_types="code"
token_endpoint_auth_method="private_key_jwt"
scopes="openid profile email"
>
<md:NameIDFormat>urn:mace:shibboleth:metadata:oidc:1.0:nameid-format:public</md:NameIDFormat>
Client which will talk to the OAuth2 Resource Server:
<oidcmd:OAuthRPExtensions
grant_types="client_credentials"
response_types="code"
token_endpoint_auth_method="client_secret_basic"
scopes="openid profile email"
>
<md:NameIDFormat>urn:mace:shibboleth:metadata:oidc:1.0:nameid-format:public</md:NameIDFormat>
oidc-attribute-resolver.xml:
<AttributeDefinition id="subjectPublic" xsi:type="Scoped" scope="%{idp.scope}"
activationConditionRef="shibboleth.oidc.Conditions.PublicRequired">
<InputDataConnector ref="suLDAP" attributeNames="uid"/>
<AttributeEncoder xsi:type="oidc:OIDCScopedString" name="sub" />
</AttributeDefinition>
oidc-attribute-filter.xml:
<AttributeFilterPolicy id="OPENID_SCOPE">
<PolicyRequirementRule xsi:type="oidc:OIDCScope" value="openid" />
<AttributeRule attributeID="subjectPublic">
<PermitValueRule xsi:type="ANY" />
</AttributeRule>
</AttributeFilterPolicy>
<AttributeFilterPolicy id="OAUTH_SUBJECT">
<PolicyRequirementRule xsi:type="AND">
<Rule xsi:type="NOT">
<Rule xsi:type="oidc:OIDCScope" value="openid" />
</Rule>
</PolicyRequirementRule>
<AttributeRule attributeID="subjectPublic">
<PermitValueRule xsi:type="ANY" />
</AttributeRule>
</AttributeFilterPolicy>
Using authorization_code works:
* Login to RP which shows scoped sub and via the access_token I can
confirm it:
$ curl -H "Authorization: Bearer $ACCESS_TOKEN" https://op.example.com/idp/profile/oidc/userinfo
{"sub":"simlu at su.se","email_verified":true,"name":"Simon Lundström","preferred_username":"simlu","given_name":"Simon","family_name":"Lundström","email":"simlu at su.se"}
Using client_credentials doesn't:
$ curl -s -u "test-user:$CLIENT_SECRET" -d "grant_type=client_credentials" -d scope=openid 'https://op.example.com/idp/profile/oidc/token' | jq -r .access_token | jwt decode -
[...]
Token claims
------------
[...]
"scope": "openid",
"sub": "test-user"
$ ACCESS_TOKEN=$(curl -s -u "test-user:$CLIENT_SECRET" -d "grant_type=client_credentials" -d scope=openid 'https://op.example.com/idp/profile/oidc/token' | jq -r .access_token)
$ curl -s -H "Authorization: Bearer $ACCESS_TOKEN" https://op.example.com/idp/profile/oidc/userinfo
{"sub":"test-user"}
$ curl -s -u "test-user:$CLIENT_SECRET" -d "grant_type=client_credentials" 'https://op.example.com/idp/profile/oidc/token' | jq -r .access_token | jwt decode -
[...]
Token claims
------------
[...]
"sub": "test-user"
It feels like I'm missing something obvious.
1,
<https://shibboleth.atlassian.net/wiki/spaces/IDPPLUGINS/pages/1376879208/OPSubClaim>
Have a great weekend!
BR,
- Simon
____________________________________
Simon Lundström
IT Services
Stockholm University
SE-106 91 Stockholm, Sweden
www.su.se/it
https://www.su.se/english/about-this-website-1.517563
More information about the users
mailing list