[Openid-specs-ab] OpenID Connect Key Binding vs OpenID Connect UserInfo Verifiable Credentials

Dick Hardt dick.hardt at gmail.com
Tue Sep 9 20:54:59 UTC 2025


I was considering specifying a KB-JWT where the PoP is not part of another
protocol

On Tue, Sep 9, 2025 at 7:10 PM <george at practicalidentity.com> wrote:

>
>
> On Sep 9, 2025, at 2:03 PM, Dick Hardt <dick.hardt at gmail.com> wrote:
>
> > And then add new code for presentation of the id_token to RP2 using DPoP
>
> why would this be DPoP?
>
> This would be protocol specific between RP1 and RP2
>
>
> I guess I assumed that RP1 and RP2 would be code written by different
> companies and so having a standard to specify how the recipient (RP2)
> verifies the ‘cnf’ claim in the token would be helpful. Otherwise, if RP2
> is a SaaS app, then it might have to implement many different proprietary
> mechanisms depending on the different RP1’s it was interacting with.
>
>
> On Tue, Sep 9, 2025 at 6:49 PM <george at practicalidentity.com> wrote:
>
>>
>>
>> On Sep 9, 2025, at 12:54 PM, Dick Hardt <dick.hardt at gmail.com> wrote:
>>
>> I agree there is potential for RP1 to share info it should not share with
>> RP2
>>
>> It is unclear that the extra complexity of asking for a new token is
>> going to prevent that any better than an OP deciding it won't issue an
>> id_token with key binding. IE the OP making a decision about what is in an
>> id_token and not in another token seems complicated for most OPs.
>>
>>
>> Maybe I’m missing the complexity. If the spec defines the claims of the
>> new token, then it can limit the privacy implications of the issued tokens
>> (e.g. the token contains ’iss’, ’sub’, ‘amr’, …). It doesn’t have to
>> include email address or other PII claims as the receiving entity just
>> wants proof of the user’s authentication status. In fact you might not even
>> need ’sub’ and could potentially use a ’session id’ instead. Though this
>> probably depends on the deployment requirements.
>>
>> More importantly to me, I am concerned about the lift to educate
>> implementers about a new token and a new way to ask for it. I suspect the
>> lack of interest in the OpenID Connect UserInfo Verifiable Credentials is
>> due to the complexity.
>>
>>
>> Won’t we need to communicate to implementors a new scope to pass as part
>> of the authentication request? And then add new code for presentation of
>> the id_token to RP2 using DPoP? We also need to communicate to the OPs the
>> special logic they need to consider based on the PII claims being issued
>> into the id_token as to whether it should add the ‘cnf’ claim or not?
>>
>>
>> I also don't think we are overloading the id_token -- but we can agree to
>> differ in our opinion on that.
>>
>> 👍 😀
>>
>>
>>
>>
>> On Tue, Sep 9, 2025 at 1:51 PM <george at practicalidentity.com> wrote:
>>
>>> Hi Dick,
>>>
>>> Thanks for answering my question (again) :)
>>>
>>> I don’t feel like the argument of “the OP can’t prevent the RP from
>>> doing X” is a viable one for specifications. The spec should
>>> encourage/push/require the RP to “do the right thing”. I believe the
>>> current model makes is easy for the RP to “do the WRONG thing”.
>>>
>>> Use cases where the id_token flows across trust boundaries make no
>>> sense. The ’sub’ claim, in many cases, has no relevance in the second trust
>>> domain as the identifier for the user is different (GUID in trust domain A
>>> does NOT equal the user in trust domain B). This is why the identity
>>> chaining spec leveraged the JSON Authorization Grant to allow the AS in
>>> trust domain A to correctly identify the user in trust domain B.
>>>
>>> Also, trying to leverage user consent as a way to solve this is really a
>>> “user experience dark pattern”. What the user is trying to do will NOT work
>>> if they don’t consent. What if the RP requesting the id_token also needs to
>>> know some personal health information and wants that in the id_token. Now
>>> the user is sharing that data with whoever the RP decides? Or has to make
>>> the choice between sharing or reduced functionality?
>>>
>>> I’m still confused why a dedicated token for authentication/status is
>>> not a viable option? If the RP is a web based RP, then requesting this
>>> token from the AS either during the authentication front channel flow, or
>>> via a backchannel flow like ID-JAG shouldn’t be an issue. For me, just
>>> because the RP is already getting the id_token doesn’t mean we should
>>> overload the id_token semantic.
>>>
>>> Thanks,
>>> George
>>>
>>> George Fletcher
>>> Identity Standards Architect
>>> Practical Identity LLC
>>>
>>>
>>>
>>> On Sep 9, 2025, at 1:16 AM, Dick Hardt <dick.hardt at gmail.com> wrote:
>>>
>>> Hi George
>>>
>>> My answer to your question:
>>>
>>> If RP1 requests claims that MUST NOT be sent to RP2, then what is the OP
>>> supposed to do? Reject the request? Send back an id_token that is
>>> insufficient for the client (RP1) but good for RP2? Send back an id_token
>>> that works for both the client and RP2 which has leaked privacy claims?
>>>
>>>
>>> Is that the OP can't prevent RP1 from sending whatever it wants to RP2.
>>> If the OP does not trust RP1 to do the right thing, then perhaps the OP
>>> should not issue an id_token to RP1?
>>>
>>> I have added the following content to the Privacy Considerations and
>>> added you to the acknowledgements:
>>>
>>> > Public key bound ID Tokens will often contain personal (PII). The RP
>>> SHOULD obtain user consent before sharing a Public key bound ID Token that
>>> contains PII with a third party.
>>>
>>> On Tue, Sep 9, 2025 at 1:57 AM <george at practicalidentity.com> wrote:
>>>
>>>> Ok, let me restate. RP1 is an OpenID Connect Relying Party acting as an
>>>> OAuth 2.0 client. This is exactly how OpenID Connect defines the Relying
>>>> Party term.
>>>>
>>>> Relying Party (RP)OAuth 2.0 Client application requiring End-User
>>>> Authentication and Claims from an OpenID Provider.
>>>> The point is not what RP1 can or cannot do with data it receives. Just
>>>> because RP1 CAN send the id_token to another party doesn’t mean is SHOULD.
>>>> The point is to define a mechanism that maximizes the likelihood RP1 will
>>>> “do the right thing”. Which from what I understand, is that RP1 needs to
>>>> communicate information about the authentication status/context with
>>>> another entity. This information needs to be authorized by the OP and MUST
>>>> NOT leak PII about the subject that the receiving entity should not receive.
>>>>
>>>> Also, I didn’t see an answer to my question…
>>>>
>>>> If RP1 requests claims that MUST NOT be sent to RP2, then what is the
>>>> OP supposed to do? Reject the request? Send back an id_token that is
>>>> insufficient for the client (RP1) but good for RP2? Send back an id_token
>>>> that works for both the client and RP2 which has leaked privacy claims?
>>>>
>>>>
>>>> George Fletcher
>>>> Identity Standards Architect
>>>> Practical Identity LLC
>>>>
>>>>
>>>>
>>>> On Sep 8, 2025, at 7:27 AM, Dick Hardt <dick.hardt at gmail.com> wrote:
>>>>
>>>> > Finally, to Dick’s points about RP1 (really the OAuth client) sending
>>>> it to RP2, I believe that creates a mutually exclusive decision for the OP.
>>>> If the client requests claims that MUST NOT be sent to RP2, then what is
>>>> the OP supposed to do? Reject the request? Send back an id_token that is
>>>> insufficient for the client (RP1) but good for RP2? Send back an id_token
>>>> that works for both the client and RP2 which has leaked privacy claims?
>>>>
>>>> RP1 is not an OAuth client -- it is an RP. It does not want
>>>> authorization, it wants authentication. The OP cannot prevent RP1 from
>>>> sending any information it wants to RP2 independent of it being in an
>>>> id_token or in another part of the communication -- what RP1 shares with
>>>> RP2 is going to be based on business decisions, not a technical barrier of
>>>> it not being in an id_token.
>>>>
>>>> On Mon, Sep 8, 2025 at 12:17 PM <george at practicalidentity.com> wrote:
>>>>
>>>>> I’m going to  respond to a couple points from different emails here as
>>>>> it’s easier to do it in one post :)
>>>>>
>>>>> Dick asked why I didn’t object to ID-JAG as the ‘aud’ rule isn’t
>>>>> applied by the OP in that draft.
>>>>>
>>>>> For me, the reason is that back when we did OpenID Connect Core, there
>>>>> was the issue of the id_token_hint parameter which meant sending the
>>>>> id_token back to the issuer that issued it. We had the discussion at that
>>>>> time and the way I remember it, we all agreed that it was ok for the
>>>>> id_token to be sent back to its issuing OP. I believe Karl makes the same
>>>>> point. I don’t see any privacy risks here either as the OP put all the
>>>>> claims into the id_token.
>>>>>
>>>>> Now regarding the comment about the Native SSO spec, in that spec
>>>>> also, the id_token is ONLY sent to the OP that issued it. Sending the
>>>>> id_token to a different OP would be outside the intent of that spec. As for
>>>>> clients written by the same company sharing the id_token, you could say
>>>>> that is an abuse of the ’aud’ rules and in fact there has been push back
>>>>> against the Native SSO spec for it’s use of the id_token.
>>>>>
>>>>> Regarding the Kubctl use of the id_token, I worked with my colleague
>>>>> at Capital One to get this changed to using a JWT based access token as the
>>>>> purpose of the token in the K8 use case was for authorization NOT
>>>>> authentication context.
>>>>>
>>>>> Finally, to Dick’s points about RP1 (really the OAuth client) sending
>>>>> it to RP2, I believe that creates a mutually exclusive decision for the OP.
>>>>> If the client requests claims that MUST NOT be sent to RP2, then what is
>>>>> the OP supposed to do? Reject the request? Send back an id_token that is
>>>>> insufficient for the client (RP1) but good for RP2? Send back an id_token
>>>>> that works for both the client and RP2 which has leaked privacy claims?
>>>>>
>>>>> I still believe the best path forward is a dedicated token for this
>>>>> purpose. The ID-JAG model is fine, send the id_token back to the OP to
>>>>> request an authentication assertion for RP2 and get back the token to
>>>>> share. If there is concern that the OP then knows who the client is
>>>>> communicating with, then make the authentication assertion usable for
>>>>> everyone (though I daresay many enterprise IDPs would not want this
>>>>> feature).
>>>>>
>>>>> Future responses today will be limited. I’ll check back this evening
>>>>> or tomorrow morning :)
>>>>>
>>>>> George Fletcher
>>>>> Identity Standards Architect
>>>>> Practical Identity LLC
>>>>>
>>>>>
>>>>>
>>>>> On Sep 7, 2025, at 5:07 PM, Karl McGuinness <me at karlmcguinness.com>
>>>>> wrote:
>>>>>
>>>>> There are several widely deployed infrastructure use cases that
>>>>> already take an id_token as a bootstrap credential
>>>>>
>>>>>    -
>>>>>    https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens
>>>>>    -
>>>>>    https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html
>>>>>    -
>>>>>    https://cloud.google.com/iam/docs/workload-identity-federation-with-other-providers
>>>>>
>>>>> It's common for a CLI tool for example to to use something like device
>>>>> authorization grant to obtain an id_token from an enterprise IdP end
>>>>> exchange the the id_token for a backend session with a control plane.  In
>>>>> fact Okta uses OpenID Native SSO
>>>>> https://openid.net/specs/openid-connect-native-sso-1_0.html for its
>>>>> AWS CLI implementation to SSO to different AWS Accounts. These use cases
>>>>> are attempting to use the ID Token similar to a X.509 as would benefit from
>>>>> a key binding for an ID Token IMHO.  From a deployment perspective it's so
>>>>> much easier to get an ID Token from an Enterprise IdP than an X.509 for
>>>>> these scenarios.  The security benefits of supporting dynamic issued
>>>>> credentials from an IdP that can implement zero-trust security controls
>>>>> including MFA outweighs the privacy risks.
>>>>>
>>>>> There are also cross-client identity scenarios
>>>>> https://developers.google.com/identity/protocols/oauth2/cross-client-identity
>>>>> where a native app for example may have its own identity and the backend a
>>>>> different client identity but to the end-user they are the same client.
>>>>>  These deployments are a single trust domain and don't have the same
>>>>> privacy risks as cross-domain deployments
>>>>>
>>>>> With ID-JAG the id_token issuer is the processor of the token exchange
>>>>> request.  The assertion is something it issued so there wasn't a concern
>>>>> for the same party to also validate it.  We didn't want the IdP to have to
>>>>> maintain the state with a refresh token so decided to use the serialized
>>>>> authentication context in the id_token for the request as the goal was a
>>>>> chained authentication.  There is also interest to flow these claims also
>>>>> in the ID-JAG to downstream AS.  I could see ID-JAG also supporting a key
>>>>> binding for the id_token if supported by the IdP.
>>>>>
>>>>> -Karl
>>>>>
>>>>>
>>>>>
>>>>> On Sun, Sep 7, 2025 at 10:19 AM Dick Hardt via Openid-specs-ab <
>>>>> openid-specs-ab at lists.openid.net> wrote:
>>>>>
>>>>>> Hey George
>>>>>>
>>>>>> I see you (and many others) were supportive for adoption of the
>>>>>> Identity Assertion Authorization Grant
>>>>>>
>>>>>>
>>>>>> https://datatracker.ietf.org/doc/draft-parecki-oauth-identity-assertion-authz-grant/
>>>>>>
>>>>>> Where a client presents the id_token back to the OP/AS to get an
>>>>>> ID-JAG token. Clearly the "aud" in the id_token is not the OP / AS. If
>>>>>> presenting a token to a party that is not the "aud" is problematic, why did
>>>>>> you not push back on that proposal?  The AS could return a special token
>>>>>> that the client could present back later to the OP to get an ID-JAG
>>>>>> according to your logic below.
>>>>>>
>>>>>> I think some of the difference of opinion here is evident in how you
>>>>>> are describing what is happening. You are using the terms client and AS for
>>>>>> the parties, which are OAuth / authorization terms -- and suggesting the
>>>>>> id_token is being presented to an RS.
>>>>>>
>>>>>> I would agree that using the id_token for authorization is
>>>>>> problematic -- but that is not what we are proposing.
>>>>>>
>>>>>> The RP1 gets an id_token that has a bound key from the OP.
>>>>>>
>>>>>> RP1 then presents the id_token to RP2, with some proof of possession
>>>>>> mechanism specific to the RP1 -> RP2 protocol.
>>>>>>
>>>>>> All the claims in the id_token are expected to be relevant in the
>>>>>> presentation to RP2.
>>>>>>
>>>>>> As Jacob notes -- the "auth_time", "acr",  and "amr" claims could be
>>>>>> very useful to RP2.
>>>>>>
>>>>>> In other words, RP1 -> RP2 is a chained authentication event.
>>>>>>
>>>>>> /Dick
>>>>>>
>>>>>>
>>>>>> On Sat, Sep 6, 2025 at 4:02 PM <george at practicalidentity.com> wrote:
>>>>>>
>>>>>>> So I think there are a couple of things I’d like to add to this
>>>>>>> conversation.
>>>>>>>
>>>>>>> 1. The original intent/purpose of the id_token is to communicate an
>>>>>>> identity assertion to the requesting client about the logged in user. Hence
>>>>>>> the ‘aud’ claim in the id_token being the client_id of the requesting
>>>>>>> client. General JWT based ‘aud’ claim processing rules require another
>>>>>>> party receiving the id_token to reject it because the ‘aud’ claim doesn’t
>>>>>>> match the receiver (see section 4.1.3 of RFC 7519). I have significant
>>>>>>> concerns around trying to re-purpose the id_token for something different
>>>>>>> (communicating status of the authentication to another party than the
>>>>>>> requesting client).
>>>>>>>
>>>>>>> 2. The need for a secure way for the client to communicate to
>>>>>>> another party (e.g. resource server) status about the authenticated user is
>>>>>>> valid. However, I don’t believe that using the id_token is the best way to
>>>>>>> do this. That is because, in addition to the ‘aud’ claim issues highlighted
>>>>>>> previously, there are significant privacy risks in doing so. I’ve seen many
>>>>>>> deployments that put user claims into the id_token that are NOT appropriate
>>>>>>> for a subsequent resource server. Asking the AS to limit the user claims in
>>>>>>> the id_token because it will be used to communicate authentication status
>>>>>>> to a resource server could hamper the requesting client as it now needs a
>>>>>>> different way to get those user claims that are appropriate just for
>>>>>>> itself. I would much prefer that a new token be defined for the explicit
>>>>>>> purpose of communicating authentication status. That frees up the AS to
>>>>>>> limit PII related claims in that token while providing them in the id_token
>>>>>>> to the requesting client. This also allows the AS to identify the list of
>>>>>>> ‘aud’ values matching the receiving servers or potentially removing it
>>>>>>> completely.
>>>>>>>
>>>>>>> 3. A new ’scope’, as currently proposed, can instruct the AS to
>>>>>>> return this new token. There is much precedent for scopes to trigger this
>>>>>>> type of behavior including the ‘openid’ scope itself.
>>>>>>>
>>>>>>> In my career I have rarely seen overloading intent to work out well.
>>>>>>> Maybe others have a different experience.
>>>>>>>
>>>>>>> Thanks,
>>>>>>> George
>>>>>>>
>>>>>>> George Fletcher
>>>>>>> Identity Standards Architect
>>>>>>> Practical Identity LLC
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Sep 5, 2025, at 5:18 AM, Jacob Ideskog via Openid-specs-ab <
>>>>>>> openid-specs-ab at lists.openid.net> wrote:
>>>>>>>
>>>>>>> Hi all,
>>>>>>>
>>>>>>> I'd like to chip in on the semantics of the two approaches.
>>>>>>>
>>>>>>> The way I've always interpreted the id_token vs the user info is the
>>>>>>> following:
>>>>>>>
>>>>>>> The *id_token* represents the authentication transaction that just
>>>>>>> took place. For that reason it contains important non-profile information
>>>>>>> such as auth_time, acr, amr etc which are useful for the RP when deciding
>>>>>>> if the authentication that took place is strong enough, fresh enough etc.
>>>>>>> It *can* also contain a number of useful claims about the account
>>>>>>> or the user but at a minimum it needs the subject in some form.
>>>>>>>
>>>>>>> The *user info *on the other hand is mainly governed by the profile
>>>>>>> scope and sibling scopes. It does not contain authentication specific
>>>>>>> information but rather only account specific details.
>>>>>>>
>>>>>>> In our implementation we tend to recommend adding account/user
>>>>>>> claims in the ID token if you think the RP needs them to save them the
>>>>>>> round trip for user info, but it's optional and up to the particular
>>>>>>> use-case. For instance if you intend to share the ID token as this spec
>>>>>>> proposes, then adding account claims should be weighed against the privacy
>>>>>>> posture required.
>>>>>>>
>>>>>>> That said, to me, issuing a proof based on user info is less
>>>>>>> valuable to a 3rd party as it would not contain the authentication specific
>>>>>>> details that may matter to that party as well. If nothing else, the
>>>>>>> auth_time is generally valuable. It could also convey details about how
>>>>>>> long the OP considers the session to be valid for if you chose to interpret
>>>>>>> the exp as such.
>>>>>>> Issuing a bound ID token seems more to the point if that's the main
>>>>>>> use-case we're after. If all we want to do is share user info details to
>>>>>>> another party then a credential would do.
>>>>>>>
>>>>>>> It sounds like they solve different problems and should not be mixed.
>>>>>>>
>>>>>>> Just my 2¢
>>>>>>>
>>>>>>> /Jacob Ideskog
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Thu, 21 Aug 2025 at 19:39, Dick Hardt via Openid-specs-ab <
>>>>>>> openid-specs-ab at lists.openid.net> wrote:
>>>>>>>
>>>>>>>> Here is my homework as assigned by the working group chair. :)
>>>>>>>>
>>>>>>>> KB = OpenID Connect Key Binding
>>>>>>>> UVC = OpenID Connect UserInfo Verifiable Credentials
>>>>>>>> Links to specs at bottom
>>>>>>>>
>>>>>>>>
>>>>>>>> *Tl;dr:*KB adds the key to an ID Token
>>>>>>>> UVC creates a verifiable credential with same info, but VC syntax
>>>>>>>> KB does it in one call to OP
>>>>>>>> UVC requires two calls to OP
>>>>>>>>
>>>>>>>> *Key Bound Token*
>>>>>>>> KB outputs an id_token that includes a `cnf` claim of the public key
>>>>>>>> UVC outputs a verifiable credential with a `did:jwk:ey...` claim
>>>>>>>> Both include all the same user claims
>>>>>>>>
>>>>>>>>
>>>>>>>> *Authentication Request*
>>>>>>>> - KB uses `dpop` scope as well as `dpop_jkt` parameter
>>>>>>>> - UVC uses `userinfo_credential`
>>>>>>>>
>>>>>>>> KB has extra layer of security as `dpop_jkt` provides additional
>>>>>>>> assurance between authentication request and token request
>>>>>>>>
>>>>>>>> *Token Request*
>>>>>>>> - KB - RP passes DPoP JWT as header
>>>>>>>> - UVC has no changes
>>>>>>>>
>>>>>>>> *Token Response*
>>>>>>>> - KB - OP passes back id_token that includes `cnf` claim
>>>>>>>> - UVC - OP passes back an access_token as well as c_nonce and
>>>>>>>> c_nonce_expires_in
>>>>>>>>
>>>>>>>> At this point, KB has completed the key binding ...
>>>>>>>>
>>>>>>>> *Credential Request and Response *
>>>>>>>> UVC continues on
>>>>>>>> - RP generates a verifiable credential request and passes it with
>>>>>>>> the access_token as a bearer token to the OP's credential endpoint
>>>>>>>> - OP returns a verifiable credential
>>>>>>>>
>>>>>>>>
>>>>>>>> https://dickhardt.github.io/openid-key-binding/main.html
>>>>>>>> https://github.com/dickhardt/openid-key-binding
>>>>>>>>
>>>>>>>> https://openid.net/specs/openid-connect-userinfo-vc-1_0.html
>>>>>>>>
>>>>>>>>
>>>>>>>> _______________________________________________
>>>>>>>> Openid-specs-ab mailing list
>>>>>>>> Openid-specs-ab at lists.openid.net
>>>>>>>> https://lists.openid.net/mailman/listinfo/openid-specs-ab
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> --
>>>>>>> Jacob Ideskog
>>>>>>> CTO
>>>>>>> Curity
>>>>>>> -------------------------------------------------------------------
>>>>>>> Sankt Göransgatan 66, Stockholm, Sweden
>>>>>>> <https://www.google.com/maps/search/Sankt+G%C3%B6ransgatan+66,+Stockholm,+Sweden?entry=gmail&source=g>
>>>>>>> M: +46 70-2233664
>>>>>>> j <jacob at twobo.com>acob at curity.io
>>>>>>> curity.io
>>>>>>> -------------------------------------------------------------------
>>>>>>> _______________________________________________
>>>>>>> Openid-specs-ab mailing list
>>>>>>> Openid-specs-ab at lists.openid.net
>>>>>>> https://lists.openid.net/mailman/listinfo/openid-specs-ab
>>>>>>>
>>>>>>>
>>>>>>> _______________________________________________
>>>>>> Openid-specs-ab mailing list
>>>>>> Openid-specs-ab at lists.openid.net
>>>>>> https://lists.openid.net/mailman/listinfo/openid-specs-ab
>>>>>>
>>>>>
>>>>>
>>>>
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.openid.net/pipermail/openid-specs-ab/attachments/20250909/682b66d7/attachment-0001.htm>


More information about the Openid-specs-ab mailing list