Clarification about passing issuer and authorization server state
Authorization Code flow defines an issuer_state parameter that is to be sent back by the Wallet to the AS in an Authorization Request and Pre-Authorized Code flow defines a pre-authorized_code parameter to be sent back by the Wallet to the AS in a Token Request.
issuer_state: OPTIONAL. String value identifying a certain processing context at the Credential Issuer. A value for this parameter is typically passed in a Credential Offer from the Credential Issuer to the Wallet (see Section 4.1). This request parameter is used to pass the issuer_state value back to the Credential Issuer.pre-authorized_code: REQUIRED. The code representing the Credential Issuer's authorization for the Wallet to obtain Credentials of a certain type. This code MUST be short lived and single use. If the Wallet decides to use the Pre-Authorized Code Flow, this parameter value MUST be included in the subsequent Token Request with the Pre-Authorized Code Flow.
While it seems logical that the pre-authorized code is handled by the AS, it is not so obvious why the issuer state is to be sent to the AS in an Authorization Request instead of to the Issuer itself.
Also, it is surprising that there is no way for the Issuer to provide an issuer_state in the Pre-Authorized Code flow. One could think of the pre-authorized code as a handle to some kind of a pre-authorized Authorization Request that itself holds an issuer state, but the spec makes no mention of this and the way for the Issuer to obtain the code is unspecified.
I think the state references carried through Credential Offer parameters should be clarified and reworked:
- The Issuer State:
- should be usable in both Authorization Code and Pre-Authorized Code flows,
- should become a parameter of the Credential Offer,
- should be made an optional parameter of the Credential Request,
- should be removed from the Authorization Request,
- should be used solely by the Issuer to retrieve some internal state relative to a specific Credential Offer, independently of the Authorization flow.
- The Pre-Authorized Code:
- should remain as-is in the Credential Offer and the Token Request,
- should be used solely by the Authorization Server to retrieve the Authentication context required to issue a Token Response,
- should be issued to the Issuer by the Authorization Server, by means remaining out of scope of this specification.
Maybe this would clarify and separate the roles of the AS and the Issuer.
Hi @edouardhue
Thanks for raising this! I had a check and can't find a contribution agreement on record for you, if you have completed one could you confirm when and under what name, and if not please complete one at https://openid.net/intellectual-property/openid-foundation-contribution-agreements/
I believe the reason there's no issuer_state in the pre-auth code flow is that it's assumed any required state is embedded in or accessible via looking up the pre authorised code.
The reason the issuer state is sent in the authorization is I believe that it's required by the authorization server to collect correct user consent and/or to know what permissions/other info to include in the access token, and because it can be embedded in the access token it's then not required to be passed to the credential issuer (and in fact to pass it there introduces new possible security issues).
Hi @jogu and @edouardhue
I have one more related question to this topic.
I believe the reason there's no issuer_state in the pre-auth code flow is that it's assumed any required state is embedded in or accessible via looking up the pre authorised code.
In the specification we can see the following:
This specification enables deployments where the Credential Issuer API and the Authorization Server are different services, perhaps even provided by different entities.
The pre-authorised code is by definition generated by the issuer. What if the AS is provided by a different entity? How can this AS validate the pre-authorized code if it does not have access to the issuer's data?
Thank you
@fabrii
What if the AS is provided by a different entity? How can this AS validate the pre-authorized code if it does not have access to the issuer's data?
For the most part the answer is the same as with classic OAuth - there needs to be some kind of agreement in place between the AS and the RS, e.g. the AS has to be aware of the capabilities of the RS in terms having some 'scope' values defined that have the same meaning to both the AS & the RS so the AS can meaningfully ask the user for consent, and the RS has to know how to validate access tokens issued by the AS.
To the particular question you asked, if the AS used for the authorization code flow doesn't have support for pre-authorised codes (which would generally necessitate it having a way for the credential issuer to generate or be issued with pre authorised codes) the solution I believe other implementors have solved this by having a second separate AS for handling pre-authorised codes.
If I understand correctly, the following approach would be allowed by the standard for this use-case:
- One shared AS, provided by a third trusted party, used by many issuers for Authorization Code Flow. These issuers must validate AS tokens, for example using the jwks endpoint.
- One custom AS per issuer, to validate the pre-authorised codes emitted by themselves.
The issuer_state and/or pre-authorised code should be embedded inside the access tokens generated by both AS. Then, this state/code can be used in the Credential Request method.
Validating tokens generated by a third party AS is simple from the issuer's perspective, as they only need access to the jwks endpoint. Doing some kind of integration between a third party AS and N issuers for validating pre-authorised codes does not seem to scale well.
Hi @jogu and @fabrii,
I confirm there is no contribution agreement from me yet. This might take time to complete as corporate bureaucracy is involved :wink:
The approach we are considering is one shared, general-purpose Issuer and several "delegated" custom AS that should be able to engage in both "classic" Authz Code and new Pre-Authz Code flows. Hence we are concerned about strong coupling, state-sharing and implicit data flows between Issuer and AS.
I'll get back with proposals as soon as IPR issues are solved…
Great, thanks @edouardhue
FYI there was a previous dicussion on a topic I think is similar that it might be worth reading: https://github.com/openid/OpenID4VCI/issues/79
Sorry for coming back late, it's been busy here. @jogu, you should now be able to find my contribution agreement, it was submitted by Orange by the end of September.
I've been thinking much about this issue. I'm now convinced that including the issuer state inside the access token is the right choice, as it allows the Authz Server to authorize its access (like some kind of resource belonging to the Issuer) during the handling of the Authz Request and then forbids the Wallet to alter it when sending the Token Request and the Credential Offer.
I'm still worried that the way of embedding the issuer state in the access token is unspecified. Despite the handling of the issuer state by the AS is specific to the Issuer implementation(s), at least some standard claim name could be defined to ease interoperability.
Also, I'm concerned with the strong binding of the pre-authorized code and the issuer state. In the current state of the specification, as the pre-authorized code replaces the issuer state in the access token, the Issuer can not simply forget this code after sending the Credential Offer. Instead, it has to keep track of it until the access token is presented with the Credential Request in order to retrieve the actual issuer state. It seems to me that this is an issue with the separation of concerns: the pre-authz code is generated and validated by the AS, the Issuer should not have to care about it more than passing it along with the Credential Offer.
I suggest that the issuer state is made independant of the actual grant type:
- The
issuer_stateparameter of the Credential Offer would be allowed at all times. - The
issuer_stateparameter would be added to both the Authz Request and to the Token Request. - The Authz Server SHOULD ensure that the issuer state received in a token request during a pre-authz grant type is bound to the pre-authz code that is provided.
This would allow several designs, where the Authz Server or the Issuer would keep track of the binding between code and state, and still ease the implementation of the Issuer when it comes to extracting the state from the access token in the Credential Endpoint.
i don't think parameter names used in the specification influence how credential issuer and AS manage state. three suggestions in the comment above simply add one more parameter that needs to be bound to pre-auth code, so I don't think they solve any issue fundamentally and potentially introduce additional complexity. also you can always use state parameter defined in rfc6749 ....
In the current state of the specification, as the pre-authorized code replaces the issuer state in the access token, the Issuer can not simply forget this code after sending the Credential Offer.
why? as long as AS can understand pre-auth code in the token request, Issuer can forget about it after putting it in the credential offer.
Instead, it has to keep track of it until the access token is presented with the Credential Request in order to retrieve the actual issuer state.
how the AS and issuer communicate which user's session this is about depends on the implementation and should not depend on whether the parameter name is issuer_state or pre-auth code.
It seems to me that this is an issue with the separation of concerns: the pre-authz code is generated and validated by the AS, the Issuer should not have to care about it more than passing it along with the Credential Offer.
the whole design of VCI evolves around the separation of concern between AS and the issuer. and i think the current design of pre-auth code and issuer_state achieves this goal without any changes for the reasons above.