Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

recommend auth code flow using signed requests #226

Merged
merged 10 commits into from
Jan 20, 2025

Conversation

AxelNennker
Copy link
Collaborator

What type of PR is this?

Add one of the following kinds:

  • documentation

What this PR does / why we need it:

This PR recommends using signed OIDC authoritzation code flow requests.

Which issue(s) this PR fixes:

Fixes #205

@mhfoo
Copy link
Collaborator

mhfoo commented Nov 20, 2024

My apologies, I created #233, before seeing this PR. Possible to use #233 instead?
@AxelNennker @jpengar

AxelNennker and others added 2 commits November 21, 2024 10:55
Co-authored-by: Jesús Peña García-Oliva <jesus.penagarcia-oliva@telefonica.com>
@garciasolero
Copy link
Contributor

garciasolero commented Nov 21, 2024

I would like to clarify why the OIDC specification was chosen over RFC 9101. It is important because, although they are very similar, they have small but significant differences.

OIDC:

When the request parameter is used, the OpenID Connect request parameter values contained in the JWT supersede those passed using the OAuth 2.0 request syntax. However, parameters MAY also be passed using the OAuth 2.0 request syntax even when a Request Object is used; this would typically be done to enable a cached, pre-signed (and possibly pre-encrypted) Request Object value to be used containing the fixed request parameters, while parameters that can vary with each request, such as state and nonce, are passed as OAuth 2.0 parameters.

RFC 9101:

The client MAY send the parameters included in the Request Object duplicated in the query parameters as well for backward compatibility, etc. However, the authorization server supporting this specification MUST only use the parameters included in the Request Object.

From my point of view, it is more interesting to follow RFC 9101, as it would ignore parameters that the client did not have to send and could potentially change how an authorization server processes the request.

@mhfoo
Copy link
Collaborator

mhfoo commented Nov 21, 2024

I would like to propose to append the following point regarding the sub claim for signed Request Object, as there are cases of implementation, the sub is wrongly included.

8< ------------------------
The sub (Subject) claim in the signed Request Object MUST NOT be present, to prevent cross-JWT confusion.

Copy link
Collaborator

@mhfoo mhfoo left a comment

Choose a reason for hiding this comment

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

Added proposed changes, including references

@jpengar
Copy link
Collaborator

jpengar commented Dec 17, 2024

Pending topics:

  • OIDC vs. RFC 9101 as a reference. Telefónica proposes to follow RF9101.
  • aud value. Telefónica believes that the aud field should be the issuer of the authorization server as per standards (both OIDC and RFC 9101)
  • sub claim requirement to avoid cross-JWT confusion.

Could we please resume and close these topics as it's under the Spring25 scope.
CC @garciasolero @AxelNennker @mhfoo @eric-murray

@mhfoo
Copy link
Collaborator

mhfoo commented Dec 18, 2024

  • aud value. Telefónica believes that the aud field should be the issuer of the authorization server as per standards (both OIDC and RFC 9101)

Referring to the aud value statement in ODIC:
"The aud value SHOULD be or include the OP's Issuer Identifier URL."

Breaking it down:
a) The aud value SHOULD be the OP's Issuer Identifier URL, or
b) The aud value SHOULD include the OP's Issuer Identifier URL.

Point b) is read as "OP's Issuer Identifier URL" is used as a prefix.

@mhfoo
Copy link
Collaborator

mhfoo commented Dec 18, 2024

  • sub claim requirement to avoid cross-JWT confusion.

Referring to OIDC, Client Authentication, private_key_jwt, the following statement is present:
"The JWT MAY contain other Claims. Any Claims used that are not understood MUST be ignored."

If this signed request object (for /authorize) aud value is the OP's Issuer Identifier URL and the 'sub' happens to be populated with the client_id of the OAuth Client, this could be used as the client_assertion for the /token or bc-authorize endpoint.

@jpengar
Copy link
Collaborator

jpengar commented Jan 14, 2025

  • aud value. Telefónica believes that the aud field should be the issuer of the authorization server as per standards (both OIDC and RFC 9101)

Referring to the aud value statement in ODIC: "The aud value SHOULD be or include the OP's Issuer Identifier URL."

Breaking it down: a) The aud value SHOULD be the OP's Issuer Identifier URL, or b) The aud value SHOULD include the OP's Issuer Identifier URL.

Point b) is read as "OP's Issuer Identifier URL" is used as a prefix.

RFC9101 states: "The value of aud should be the value of the authorization server (AS) issuer..." in that sense. So again it sounds reasonable to suggest that the aud field should be the issuer of the authorisation server, taking into account both standards statements.

@AxelNennker
Copy link
Collaborator Author

I changed the text regarding "aud".
I think the conversation regarding "sub" can be closed because sub is not part of the request object.

It represents the request as a JWT whose Claims are the request parameters specified in Section 3.1.2. This JWT is called a Request Object.

@mhfoo
Copy link
Collaborator

mhfoo commented Jan 15, 2025

I think the conversation regarding "sub" can be closed because sub is not part of the request object.

@AxelNennker
I would like to suggest adding a "Note" to mention sub claim should not be part of the request object.

@AxelNennker
Copy link
Collaborator Author

For easier review: The current text is this. Can this be merged? I think it resolves all review comments and suggestions.

Signed Authentication Requests

It is RECOMMENDED that signed authentication requests be used, as specified by OIDC. The same key MAY be used for signing the authentication request as is used for client authentication.

It is RECOMMENDED that the value of the aud field of the signed authentication request is the URL of the Authorization Endpoint. The authorization server MAY accept different values of the aud field e.g. the issuer field of its OpenID Provider Metadata. The authorization server MUST check the value of the aud field and reject signed authentication requests if the value of the aud is not associated with the authorization server.

Note: Care must be taken in a multi-tenant environment that a signed authentication request for one tenant is not accepted at another tenant endpoint.

Note: For security reasons it is recommended that the API consumer never includes a sub field in the signed request object, because otherwise the signed request object might be used for client authenticaton.

@garciasolero
Copy link
Contributor

For easier review: The current text is this. Can this be merged? I think it resolves all review comments and suggestions.

@AxelNennker,

I have not received feedback from your side on the comment regarding why the OIDC specification is preferred over RFC9101 when handling parameters outside the request object parameter.

@jpengar
Copy link
Collaborator

jpengar commented Jan 15, 2025

For easier review: The current text is this. Can this be merged? I think it resolves all review comments and suggestions.

Signed Authentication Requests

It is RECOMMENDED that signed authentication requests be used, as specified by OIDC. The same key MAY be used for signing the authentication request as is used for client authentication.

It is RECOMMENDED that the value of the aud field of the signed authentication request is the URL of the Authorization Endpoint. The authorization server MAY accept different values of the aud field e.g. the issuer field of its OpenID Provider Metadata. The authorization server MUST check the value of the aud field and reject signed authentication requests if the value of the aud is not associated with the authorization server.

Note: Care must be taken in a multi-tenant environment that a signed authentication request for one tenant is not accepted at another tenant endpoint.

Note: For security reasons it is recommended that the API consumer never includes a sub field in the signed request object, because otherwise the signed request object might be used for client authenticaton.

I still don't understand why we change the aud value from what the standards say, both OIDC and RFC 9101. Even in the CIBA specification states that the valid value should be the issuer of the authorization server, as also mentioned by @garciasolero here.

And in the case of CIBA, the profile doesn't even mention the aud claim at all.

Copy link
Collaborator

@mhfoo mhfoo left a comment

Choose a reason for hiding this comment

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

LGTM, based on @shilpa-padgaonkar corrections.
@AxelNennker, Thanks.

@mhfoo
Copy link
Collaborator

mhfoo commented Jan 16, 2025

I still don't understand why we change the aud value from what the standards say, both OIDC and RFC 9101. Even in the CIBA specification states that the valid value should be the issuer of the authorization server, as also mentioned by @garciasolero here.

And in the case of CIBA, the profile doesn't even mention the aud claim at all.

@jpengar
Regarding the aud value, RFC 9101, section 10.3, Explicit Endpoints recommends the following

research such as [BASIN] points out that it is a good practice to explicitly state the intended interaction endpoints and the message position in the sequence in a tamper-evident manner so that the intent of the initiator is unambiguous. It is RECOMMENDED by this specification to use this practice for the following endpoints defined in [RFC6749], [RFC6750], and [RFC8414]:
(b) Authorization endpoint (authorization_endpoint)

In RFC 8725 JSON Web Token Best Current Practices, section 3.12, Use Mutually Exclusive Validation Rules for Different Kinds of JWTs, the following is indicated:

Each application of JWTs defines a profile specifying the required and optional JWT claims and the validation rules associated with them. If more than one kind of JWT can be issued by the same issuer, the validation rules for those JWTs MUST be written such that they are mutually exclusive, rejecting JWTs of the wrong kind. To prevent substitution of JWTs from one context into another, application developers may employ a number of strategies:

  • Use different "aud" values for different uses of JWTs from the same issuer. Then audience validation will reject JWTs substituted into inappropriate contexts.

The main security concern to address, is to prevent having a signed request object being used as a client_assertion in private_key_jwt OIDC client authentication, where it indicates the following for a private_key_jwt:

The JWT MAY contain other Claims. Any Claims used that are not understood MUST be ignored.

By specifying the /authorize endpoint as the aud will mitigate this security concern.

AxelNennker and others added 2 commits January 16, 2025 17:06
Co-authored-by: Shilpa Padgaonkar <77152136+shilpa-padgaonkar@users.noreply.github.com>
@AxelNennker
Copy link
Collaborator Author

AxelNennker commented Jan 16, 2025

I think now all comments and suggestions are addressed. Please review again.
@jpengar @shilpa-padgaonkar @mhfoo @garciasolero @sebdewet @eric-murray

Copy link
Collaborator

@mhfoo mhfoo left a comment

Choose a reason for hiding this comment

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

LGTM

@eric-murray
Copy link
Collaborator

I'd like to see the conflicts resolved before approving

@AxelNennker
Copy link
Collaborator Author

merge conflicts have been resolved, please review and consider approving.

Copy link
Collaborator

@sebdewet sebdewet left a comment

Choose a reason for hiding this comment

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

LGTM

@AxelNennker AxelNennker merged commit 68ebbc2 into main Jan 20, 2025
1 check passed
@jpengar
Copy link
Collaborator

jpengar commented Jan 20, 2025

I believe this PR (#226) was merged prematurely, as key concerns raised during the discussion remain unresolved. Specifically:

  • The choice between OIDC and RFC9101 and the rationale for deviating from the latter.
  • The handling of the aud parameter, which conflicts with existing specifications like CIBA.
  • Questions about how to treat parameters not included in the signed request object, as RFC9101 specifies they must be ignored.

Since these points were still under discussion and the proposed text was not agreed upon by all participants, I recommend reverting this PR until a consensus is reached and the final text is approved.

@AxelNennker @mhfoo @garciasolero @sebdewet @eric-murray @shilpa-padgaonkar

@AxelNennker
Copy link
Collaborator Author

I reverted the merge which created #249

@AxelNennker
Copy link
Collaborator Author

AxelNennker commented Jan 20, 2025

@garciasolero wrote:

I would like to clarify why the OIDC specification was chosen over RFC 9101. It is important because, although they are very similar, they have small but significant differences.

OIDC:

When the request parameter is used, the OpenID Connect request parameter values contained in the JWT supersede those passed using the OAuth 2.0 request syntax. However, parameters MAY also be passed using the OAuth 2.0 request syntax even when a Request Object is used; this would typically be done to enable a cached, pre-signed (and possibly pre-encrypted) Request Object value to be used containing the fixed request parameters, while parameters that can vary with each request, such as state and nonce, are passed as OAuth 2.0 parameters.

RFC 9101:

The client MAY send the parameters included in the Request Object duplicated in the query parameters as well for backward compatibility, etc. However, the authorization server supporting this specification MUST only use the parameters included in the Request Object.

From my point of view, it is more interesting to follow RFC 9101, as it would ignore parameters that the client did not have to send and could potentially change how an authorization server processes the request.

Sorry, @garciasolero, I thought this was addressed.

I think that 9101 and OIDC say the same thing regarding parameters existing both in the parameters and in the request object.

For compatibility reasons OAuth2 parameters that are required are also required in OIDC. But if parameters are also in the request object then those are the only ones that are relevant.
OIDC expresses this using these words: When the request parameter is used, the OpenID Connect request parameter values contained in the JWT supersede those passed using the OAuth 2.0 request syntax.
RFC9101 expresses this using these words: the authorization server supporting this specification MUST only use the parameters included in the Request Object.

You write: "From my point of view, it is more interesting to follow RFC 9101, as it would ignore parameters that the client did not have to send and could potentially change how an authorization server processes the request."

Maybe an some examples: (with line wraps within values for display purposes only)

Valid example. All required parameters are in the request AND the request object.

GET /authorize?
    request=base64urlencode(signed({
  "response_type": "code",
  "scope": "openid%20profile%20email",
  "client_id": "s6BhdRkqt3",
  "redirect_uri": "https%3A%2F%2Fclient.example.org%2Fcb"
})
    &response_type=code
    &scope=openid%20profile%20email
    &client_id=s6BhdRkqt3
    &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb HTTP/1.1
Host: server.example.com

Invalid OIDC example because required parameters are missing although they provided in the request object

GET /authorize?
    request=base64urlencode(signed({
  "response_type": "code",
  "scope": "openid%20profile%20email",
  "client_id": "s6BhdRkqt3",
  "redirect_uri": "https%3A%2F%2Fclient.example.org%2Fcb"
}) HTTP/1.1
Host: server.example.com

This PR does not change the OIDC standard, it just RECOMMENDS signed request objects.

@AxelNennker
Copy link
Collaborator Author

Regarding aud:

CIBA writes:

To address that ambiguity the Issuer Identifier of the OP SHOULD be used as the value of the audience. To facilitate interoperability, the OP MUST accept its Issuer Identifier, Token Endpoint URL, or Backchannel Authentication Endpoint URL as values that identify it as an intended audience.

We recommend:
This document RECOMMENDS that for OIDC CIBA the audience SHOULD be the Backchannel Authentication Endpoint.

This PR writes:

It is RECOMMENDED that the value of the aud field of the signed authentication request is the URL of the Authorization Endpoint.

This PR does what RFC9101 recommends, that is, the aud value is the URL of the endpoint the request is send to. While allowing other values to be used as long as the Authorization Server validates that the used value for "aud" is itself.

For interoperability reasons CIBA and OIDC are quite lenient regarding aud values. The responsibility in both flows lies with the authorization server. The profile recommends what rfc9101 recommends.

I thought that the above was the agreement here. Sorry, if I misinterpreted that.

@jpengar
Copy link
Collaborator

jpengar commented Jan 20, 2025

Regarding aud:

CIBA writes:

To address that ambiguity the Issuer Identifier of the OP SHOULD be used as the value of the audience. To facilitate interoperability, the OP MUST accept its Issuer Identifier, Token Endpoint URL, or Backchannel Authentication Endpoint URL as values that identify it as an intended audience.

We recommend: This document RECOMMENDS that for OIDC CIBA the audience SHOULD be the Backchannel Authentication Endpoint.

This PR writes:

It is RECOMMENDED that the value of the aud field of the signed authentication request is the URL of the Authorization Endpoint.

This PR does what RFC9101 recommends, that is, the aud value is the URL of the endpoint the request is send to. While allowing other values to be used as long as the Authorization Server validates that the used value for "aud" is itself.

For interoperability reasons CIBA and OIDC are quite lenient regarding aud values. The responsibility in both flows lies with the authorization server. The profile recommends what rfc9101 recommends.

I thought that the above was the agreement here. Sorry, if I misinterpreted that.

@AxelNennker As was mentioned by @garciasolero here, the recommendations apply depending on the type of assertion, client authentication assertion (client_assertion) or signed request object assertion.

The CAMARA profile specifies "This document RECOMMENDS that for OIDC Authorisation Code Flow and OAuth2 Client Credentials Grant the audience SHOULD be the URL of the Authorisation Server's Token Endpoint. This document RECOMMENDS that for OIDC CIBA the audience SHOULD be the Backchannel Authentication Endpoint", but this is for client authentication. Also, for the Auth code flow, note that there is no client authentication in the /authorize endpoint (only in /token).

And in the CAMARA profile document, in the case of CIBA, there is not even a mention of the aud parameter for the signed authentication request. So it relies on what the CIBA standard says, which is The Audience claim MUST contain the value of the Issuer Identifier for the OP, which identifies the Authorization Server as an intended audience.". So, we would be recommending different things to Auth code flow and CIBA.

@jpengar jpengar deleted the recommend_auth_code_flow_signed-_requests branch January 24, 2025 09:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Spring25: Proposal to RECOMMEND the use of Signed Request Object for the /authorize endpoint to prevent abuse
7 participants