Skip to content

add redirect_uri to Credential Offer#759

Open
paulbastian wants to merge 9 commits into
mainfrom
pb/redirect
Open

add redirect_uri to Credential Offer#759
paulbastian wants to merge 9 commits into
mainfrom
pb/redirect

Conversation

@paulbastian

@paulbastian paulbastian commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Replaces #723

  • discuss whether to use SHOULD or MAY to open redirect_uri
  • make editorial changes to fix grants text in Credential Offer Parameters in 1.0?

@dpostnikov
dpostnikov requested review from GarethCOliver and fkj June 22, 2026 08:06
Comment thread 1.1/openid-4-verifiable-credential-issuance-1_1.md Outdated
Comment on lines +394 to +403
* `authorization_code`: OPTIONAL. Object for the Authorization Code Grant type.
* `issuer_state`: OPTIONAL. String value created by the Credential Issuer and opaque to the Wallet that is used to bind the subsequent Authorization Request with a context set up during previous process steps. If the Wallet decides to use the Authorization Code Flow and received a value for this parameter, it MUST include it in the subsequent Authorization Request to the Authorization Server as the `issuer_state` parameter value.
* `authorization_server`: OPTIONAL string that the Wallet can use to identify the Authorization Server to use with this grant type when `authorization_servers` parameter in the Credential Issuer metadata has multiple entries. It MUST NOT be used otherwise. The value of this parameter MUST match with one of the values in the `authorization_servers` array obtained from the Credential Issuer metadata.
* `urn:ietf:params:oauth:grant-type:pre-authorized_code`: OPTIONAL. Object for the Pre-authorized Code Grant type.
* `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.
* `tx_code`: OPTIONAL. Object indicating that a Transaction Code is required if present, even if empty. It describes the requirements for a Transaction Code, which the Authorization Server expects the End-User to present along with the Token Request in a Pre-Authorized Code Flow. If the Authorization Server does not expect a Transaction Code, this object is absent; this is the default. The Transaction Code is intended to bind the Pre-Authorized Code to a certain transaction to prevent replay of this code by an attacker that, for example, scanned the QR code while standing behind the legitimate End-User. It is RECOMMENDED to send the Transaction Code via a separate channel. If the Wallet decides to use the Pre-Authorized Code Flow, the Transaction Code value MUST be sent in the `tx_code` parameter with the respective Token Request as defined in (#token-request). If no `length`, `description`, or `input_mode` is given, this object MAY be empty.
* `input_mode` : OPTIONAL. String specifying the input character set. Possible values are `numeric` (only digits) and `text` (any characters). The default is `numeric`.
* `length`: OPTIONAL. Integer specifying the length of the Transaction Code. This helps the Wallet to render the input screen and improve the user experience.
* `description`: OPTIONAL. String containing guidance for the Holder of the Wallet on how to obtain the Transaction Code, e.g., describing over which communication channel it is delivered. The Wallet is RECOMMENDED to display this description next to the Transaction Code input screen to improve the user experience. The length of the string MUST NOT exceed 300 characters. The `description` does not support internationalization, however the Issuer MAY detect the Holder's language by previous communication or an HTTP Accept-Language header within an HTTP GET request for a Credential Offer URI.
* `authorization_server`: OPTIONAL string that the Wallet can use to identify the Authorization Server to use with this grant type when `authorization_servers` parameter in the Credential Issuer metadata has multiple entries. It MUST NOT be used otherwise. The value of this parameter MUST match with one of the values in the `authorization_servers` array obtained from the Credential Issuer metadata.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for reviews: this part is only moved upwards

Comment thread 1.1/openid-4-verifiable-credential-issuance-1_1.md Outdated
Comment thread 1.1/openid-4-verifiable-credential-issuance-1_1.md Outdated
Comment thread 1.1/openid-4-verifiable-credential-issuance-1_1.md Outdated
Comment thread 1.1/openid-4-verifiable-credential-issuance-1_1.md Outdated
Comment thread 1.1/openid-4-verifiable-credential-issuance-1_1.md Outdated

Because the Credential Offer is not signed and its origin is not authenticated, an attacker could inject or manipulate the `redirect_uri` Credential Offer parameter (#credential-offer-parameters) to direct the End-User to an attacker-controlled website, for example, to conduct a phishing attack while leveraging the End-User's trust that they are returning to the Credential Issuer.

To mitigate this, the Wallet relies on the `expected_redirect_origin` Credential Issuer metadata parameter (#credential-issuer-parameters). Unlike the Credential Offer, the Credential Issuer metadata is retrieved by the Wallet directly from the Credential Issuer over a TLS-protected connection (#credential-issuer-wellknown) and is therefore authenticated. Before offering the End-User the option to follow the `redirect_uri`, the Wallet MUST verify that the origin of the `redirect_uri` matches one of the origins listed in `expected_redirect_origin`, and MUST discard the `redirect_uri` otherwise, including when `expected_redirect_origin` is absent.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The guidance here is just repeating normative requirements above. Is it necessary?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concern is mainly that readers will get confused about whether this is really a new requirement and think they have misunderstood something. Also, for certification it is annoying and confusing to have duplicate requirements.
My suggestion would be to try to merge the first sentence explaining the reason into the main text.

Comment on lines 396 to 399

Additional Credential Offer parameters MAY be defined and used.
The Wallet MUST ignore any unrecognized parameters.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these editorial changes move these two lines further down, which currently split the grants parameter from the grants values

* `nonce_endpoint`: OPTIONAL. URL of the Credential Issuer's Nonce Endpoint, as defined in (#nonce-endpoint). This URL MUST use the `https` scheme and MAY contain port, path, and query parameter components. If omitted, the Credential Issuer does not require the use of `c_nonce`.
* `deferred_credential_endpoint`: OPTIONAL. URL of the Credential Issuer's Deferred Credential Endpoint, as defined in (#deferred-credential-issuance). This URL MUST use the `https` scheme and MAY contain port, path, and query parameter components. If omitted, the Credential Issuer does not support the Deferred Credential Endpoint.
* `notification_endpoint`: OPTIONAL. URL of the Credential Issuer's Notification Endpoint, as defined in (#notification-endpoint). This URL MUST use the `https` scheme and MAY contain port, path, and query parameter components. If omitted, the Credential Issuer does not support the Notification Endpoint.
* `expected_redirect_origin`: REQUIRED if the Credential Issuer uses the `redirect_uri` Credential Offer parameter (#credential-offer-parameters), and otherwise omitted. A non-empty array of strings, where each string is an origin, as defined in [@RFC6454], that the Credential Issuer may use in the `redirect_uri` of the Credential Offer. If a Credential Offer contains a `redirect_uri`, the Wallet MUST NOT utilize it unless the origin of the `redirect_uri` matches one of the values in this array. If this parameter is omitted, the Wallet MUST ignore any `redirect_uri` present in a Credential Offer from this Credential Issuer.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably be plural as it's an array? and this is a normative reference I think, so !

Suggested change
* `expected_redirect_origin`: REQUIRED if the Credential Issuer uses the `redirect_uri` Credential Offer parameter (#credential-offer-parameters), and otherwise omitted. A non-empty array of strings, where each string is an origin, as defined in [@RFC6454], that the Credential Issuer may use in the `redirect_uri` of the Credential Offer. If a Credential Offer contains a `redirect_uri`, the Wallet MUST NOT utilize it unless the origin of the `redirect_uri` matches one of the values in this array. If this parameter is omitted, the Wallet MUST ignore any `redirect_uri` present in a Credential Offer from this Credential Issuer.
* `expected_redirect_origins`: REQUIRED if the Credential Issuer uses the `redirect_uri` Credential Offer parameter (#credential-offer-parameters), and otherwise omitted. A non-empty array of strings, where each string is an origin, as defined in [@!RFC6454], that the Credential Issuer may use in the `redirect_uri` of the Credential Offer. If a Credential Offer contains a `redirect_uri`, the Wallet MUST NOT utilize it unless the origin of the `redirect_uri` matches one of the values in this array. If this parameter is omitted, the Wallet MUST ignore any `redirect_uri` present in a Credential Offer from this Credential Issuer.


Because the parameter values in the Credential Offer are not trustworthy, an attacker could inject or manipulate the `redirect_uri` Credential Offer parameter (#credential-offer-parameters) to direct the End-User to an attacker-controlled website, for example, to conduct a phishing attack while leveraging the End-User's trust that they are returning to the Credential Issuer.

To mitigate this, the Wallet relies on the `expected_redirect_origin` Credential Issuer metadata parameter (#credential-issuer-parameters). Unlike the Credential Offer, the Credential Issuer metadata is retrieved by the Wallet directly from the Credential Issuer over a TLS-protected connection (#credential-issuer-wellknown) and is therefore authenticated. Before offering the End-User the option to follow the `redirect_uri`, the Wallet MUST verify that the origin of the `redirect_uri` matches one of the origins listed in `expected_redirect_origin`, and MUST discard the `redirect_uri` otherwise, including when `expected_redirect_origin` is absent.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Before offering the End-User the option" is inconsistent with the text elsewhere saying "MAY ask the End-User for consent before navigating" - the check needs to happen even if the wallet decides to proceed without offering the user the option.

* `nonce_endpoint`: OPTIONAL. URL of the Credential Issuer's Nonce Endpoint, as defined in (#nonce-endpoint). This URL MUST use the `https` scheme and MAY contain port, path, and query parameter components. If omitted, the Credential Issuer does not require the use of `c_nonce`.
* `deferred_credential_endpoint`: OPTIONAL. URL of the Credential Issuer's Deferred Credential Endpoint, as defined in (#deferred-credential-issuance). This URL MUST use the `https` scheme and MAY contain port, path, and query parameter components. If omitted, the Credential Issuer does not support the Deferred Credential Endpoint.
* `notification_endpoint`: OPTIONAL. URL of the Credential Issuer's Notification Endpoint, as defined in (#notification-endpoint). This URL MUST use the `https` scheme and MAY contain port, path, and query parameter components. If omitted, the Credential Issuer does not support the Notification Endpoint.
* `expected_redirect_origin`: REQUIRED if the Credential Issuer uses the `redirect_uri` Credential Offer parameter (#credential-offer-parameters), and otherwise omitted. A non-empty array of strings, where each string is an origin, as defined in [@RFC6454], that the Credential Issuer may use in the `redirect_uri` of the Credential Offer. If a Credential Offer contains a `redirect_uri`, the Wallet MUST NOT utilize it unless the origin of the `redirect_uri` matches one of the values in this array. If this parameter is omitted, the Wallet MUST ignore any `redirect_uri` present in a Credential Offer from this Credential Issuer.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we start from what I believe is the intended attacker model (that the attacker can modify the credential offer) then only authenticating the origin feels very lenient. e.g. if https://www.microsoft.com was listed here I'd be pretty certain there's some url somewhere on the website that I can sent the user to as useful part of some wider phishing attack or similar.

Comment thread 1.1/openid-4-verifiable-credential-issuance-1_1.md Outdated
Comment thread 1.1/openid-4-verifiable-credential-issuance-1_1.md Outdated
* `length`: OPTIONAL. Integer specifying the length of the Transaction Code. This helps the Wallet to render the input screen and improve the user experience.
* `description`: OPTIONAL. String containing guidance for the Holder of the Wallet on how to obtain the Transaction Code, e.g., describing over which communication channel it is delivered. The Wallet is RECOMMENDED to display this description next to the Transaction Code input screen to improve the user experience. The length of the string MUST NOT exceed 300 characters. The `description` does not support internationalization, however the Issuer MAY detect the Holder's language by previous communication or an HTTP Accept-Language header within an HTTP GET request for a Credential Offer URI.
* `authorization_server`: OPTIONAL string that the Wallet can use to identify the Authorization Server to use with this grant type when `authorization_servers` parameter in the Credential Issuer metadata has multiple entries. It MUST NOT be used otherwise. The value of this parameter MUST match with one of the values in the `authorization_servers` array obtained from the Credential Issuer metadata.
* `redirect_uri`: OPTIONAL. String that is a URL using the `https` scheme. If present, the Wallet SHOULD navigate the User Agent to this URL after it has finished processing the Credential Offer, e.g. to return to the Credential Issuer's website. The Wallet SHOULD do this regardless of whether the issuance succeeded, failed, was cancelled or entered a pending state. The Issuer MUST NOT rely on the redirect occurring for successful issuance. The Wallet MAY ask the End-User for consent before navigating. Because the Credential Offer is not authenticated (see (#credential-offer-security)), the Wallet MUST validate this value against the `expected_redirect_origin` Credential Issuer metadata parameter as defined in (#credential-issuer-parameters) and MUST ignore the `redirect_uri` if the validation fails.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"the" User Agent probably isn't correct. In the cross device case it definitely won't be the user agent that was used earlier in the flow, and even in the same device case that isn't guaranteed.

Not sure exactly what it should say. It's probably sensible to make some recommendation about launching in the system browser vs using an in-app browser tab, but I don't know what is best.

Comment on lines 404 to 405
* `redirect_uri`: OPTIONAL. String that is a URL using the `https` scheme. If present, the Wallet SHOULD navigate the User Agent to this URL after it has finished processing the Credential Offer, e.g. to return to the Credential Issuer's website. The Wallet SHOULD do this regardless of whether the issuance succeeded, failed, was cancelled or entered a pending state. The Issuer MUST NOT rely on the redirect occurring for successful issuance. The Wallet MAY ask the End-User for consent before navigating. Because the Credential Offer is not authenticated (see (#credential-offer-security)), the Wallet MUST validate this value against the `expected_redirect_origin` Credential Issuer metadata parameter as defined in (#credential-issuer-parameters) and MUST ignore the `redirect_uri` if the validation fails.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after it has finished processing the Credential Offer

The Wallet SHOULD do this regardless of whether the issuance succeeded, failed, was cancelled or entered a pending state.

These statement feels quite ambiguous. 'pending state' we don't use anywhere else in the spec and I think could be clearer (I presume it might be referring to deferred issuance, but not sure if it's intended to encompass other things too). 'finished processing the Credential Offer' feels like it doesn't really match with 'issuance succeeded'.

}
}
},
"redirect_uri": "https://credential-issuer.example.com/return?issuer_state=eyJhbGciOiJSU0Et...FYUaBy"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes me feel uneasy. If issuer_state is likely to be something important here, it feels like we should guarantee that the issuer_state passed to the redirect_uri is the one that was used in this session? (i.e. that it hasn't been switched out by an attacker.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it help to remove the issuer_state query parameter?

Another use case is when the Credential Issuer uses cryptographic schemes that can provide binding to the End-User possessing that Credential without explicit cryptographic material being supplied by the application used by that End-User. For example, in the case of the BBS Signature Scheme, the issued Credential itself is a secret and only a derivation from the Credential is presented to the Verifier. Effectively, the Credential is bound to the Credential Issuer's signature on the Credential, which becomes a shared secret transferred from the Credential Issuer to the End-User.

If Cryptographic Key Binding is not required, the Credential Issuer omits the `cryptographic_binding_methods_supported` parameter in the `credential_configurations_supported` object in the Credential Issuer Metadata as defined in (#credential-issuer-parameters). As a consequence, the `proof_types_supported` parameter is also absent, indicating to the Wallet to omit the `proofs` parameter in the Credential Request.
If Cryptographic Key Binding is not required, the Credential Issuer omits the `cryptographic_binding_methods_supported` parameter in the `credential_configurations_supported` object in the Credential Issuer Metadata as defined in (#credential-issuer-parameters). As a consequence, the `proof_types_supported` parameter is also absent, indicating to the Wallet to omit the `proofs` parameter in the Credential Request.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What the heck changed here? I can't see any changes?

@paulbastian paulbastian Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no clue either, I don't remember changing something here

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somehow commit 95c9ce3 added a windows line ending (\r\n) on this line (the only one in the whole file) - this change just removes it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so okay to keep this?

Comment thread 1.1/openid-4-verifiable-credential-issuance-1_1.md Outdated
paulbastian and others added 2 commits July 16, 2026 17:13
Co-authored-by: Paul Bastian <paul.bastian@posteo.de>
Co-authored-by: Joseph Heenan <joseph@authlete.com>
Comment thread 1.1/openid-4-verifiable-credential-issuance-1_1.md Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants