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

Adds server-provided nonces for Client Attestation PoP JWT freshness verification #64

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

pmhsfelix
Copy link

Closes #59

📑 Description

This PR adds server-provided nonces as a way to check the freshness of Client Attestation PoP JWT, in a similar way to what is done in DPoP (RFC 9449).

Preview Link

NA

@pmhsfelix pmhsfelix requested a review from tplooker as a code owner November 14, 2023 14:08
An authorization server MAY require the use of nonces.
This requirement is communicated to the client by having the authorization server return an HTTP response with the `400` status and an `error` field with the value `"use_attestation_nonce"`, when the Client Attestation PoP JWT present in the HTTP request does not contain a valid nonce.
This HTTP response MUST also contain the `Attestation-Nonce` header, with the value of the new server provided nonce.
The client MUST use this new nonce value to create a new Client Attestation PoP JWT and resend the previous request.

Choose a reason for hiding this comment

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

Suggested change
The client MUST use this new nonce value to create a new Client Attestation PoP JWT and resend the previous request.
The Client MUST use this new nonce value to create a new Client Attestation PoP JWT and resend the previous request.

@pmhsfelix I'm wondering if using Client as defined term in OAuth 2.0. If yes this change is required within the entire text

Copy link
Contributor

@tlodderstedt tlodderstedt left a comment

Choose a reason for hiding this comment

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

lgtm

Copy link

@ju-cu ju-cu left a comment

Choose a reason for hiding this comment

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

I suggest some normative language for the nonce syntax and HTTP error message. All in all good, so green light from me.


This section specifies a mechanism using server provided opaque nonces to limit the lifetime of a Client Attestation PoP JWT, similar to the one defined in section 8 of {{RFC9449}}.
Without employing such a mechanism, a malicious party controlling the client (potentially including the end-user) can create Client Attestation PoP JWT for use arbitrarily far in the future.
In addition, a mismatch between the time reference of a non-malicious client (e.g. a mobile device) and the time reference of the authorization server, may result in Client Attestation PoP JWT that are never accepted by the authorization server.
Copy link

Choose a reason for hiding this comment

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

Suggested change
In addition, a mismatch between the time reference of a non-malicious client (e.g. a mobile device) and the time reference of the authorization server, may result in Client Attestation PoP JWT that are never accepted by the authorization server.
In addition, a mismatch between the time reference of a client (e.g. a mobile device) and the time reference of the authorization server, may result in Client Attestation PoP JWT that are never accepted by the authorization server.

Without employing such a mechanism, a malicious party controlling the client (potentially including the end-user) can create Client Attestation PoP JWT for use arbitrarily far in the future.
In addition, a mismatch between the time reference of a non-malicious client (e.g. a mobile device) and the time reference of the authorization server, may result in Client Attestation PoP JWT that are never accepted by the authorization server.

Including a nonce value contributed by the authorization server in the Client Attestation PoP JWT MAY be used by authorization servers to limit the lifetime of those proofs, both protecting against attacks and allowing for interoperability with clients with different time references.
Copy link

Choose a reason for hiding this comment

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

Suggested change
Including a nonce value contributed by the authorization server in the Client Attestation PoP JWT MAY be used by authorization servers to limit the lifetime of those proofs, both protecting against attacks and allowing for interoperability with clients with different time references.
Including a nonce value provided by the authorization server in the Client Attestation PoP JWT MAY be used by authorization servers to limit the lifetime of those proofs, both protecting against attacks and allowing for interoperability with clients with different time references.

"provided" because it maps the header.

Including a nonce value contributed by the authorization server in the Client Attestation PoP JWT MAY be used by authorization servers to limit the lifetime of those proofs, both protecting against attacks and allowing for interoperability with clients with different time references.

An authorization server MAY require the use of nonces.
This requirement is communicated to the client by having the authorization server return an HTTP response with the `400` status and an `error` field with the value `"use_attestation_nonce"`, when the Client Attestation PoP JWT present in the HTTP request does not contain a valid nonce.
Copy link

Choose a reason for hiding this comment

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

Use formative language:

Suggested change
This requirement is communicated to the client by having the authorization server return an HTTP response with the `400` status and an `error` field with the value `"use_attestation_nonce"`, when the Client Attestation PoP JWT present in the HTTP request does not contain a valid nonce.
If the authorization server requires the use of nonces, the authorization server MUST return an HTTP response with the `400` status and an `error` field with the value `"use_attestation_nonce"`, when the Client Attestation PoP JWT present in the HTTP request does not contain a valid nonce.

This requirement is communicated to the client by having the authorization server return an HTTP response with the `400` status and an `error` field with the value `"use_attestation_nonce"`, when the Client Attestation PoP JWT present in the HTTP request does not contain a valid nonce.
This HTTP response MUST also contain the `Attestation-Nonce` header, with the value of the new server provided nonce.
The client MUST use this new nonce value to create a new Client Attestation PoP JWT and resend the previous request.
The server provided nonce must be included in the `nonce` JWT payload claim, as a JSON string.
Copy link

Choose a reason for hiding this comment

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

Suggested change
The server provided nonce must be included in the `nonce` JWT payload claim, as a JSON string.
The server provided nonce must be included in the `nonce` JWT payload claim, as a string.

Doesn't "JWT payload claim" imply that the data is JSON?

}
~~~

Authorization servers MAY also include the `Attestation-Nonce` in success responses, as a way to communicate newer nonces values to clients.
Copy link

Choose a reason for hiding this comment

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

Suggested change
Authorization servers MAY also include the `Attestation-Nonce` in success responses, as a way to communicate newer nonces values to clients.
Authorization servers MAY also include the `Attestation-Nonce` in success responses, as a way to communicate newer nonce values to clients.


Authorization servers MAY also include the `Attestation-Nonce` in success responses, as a way to communicate newer nonces values to clients.
Clients SHOULD keep using a nonce received in a `Attestation-Nonce` header until a newer value is received via this header, both on success or non-success responses.
Responses that include the `Attestation-Nonce` HTTP header should be uncacheable (e.g., using `Cache-Control: no-store` in response to a GET request) to prevent the response from being used to serve a subsequent request and a stale nonce value from being used as a result.
Copy link

Choose a reason for hiding this comment

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

Suggested change
Responses that include the `Attestation-Nonce` HTTP header should be uncacheable (e.g., using `Cache-Control: no-store` in response to a GET request) to prevent the response from being used to serve a subsequent request and a stale nonce value from being used as a result.
Responses that include the `Attestation-Nonce` HTTP header should be uncacheable (e.g., using `Cache-Control: no-store` in response to a GET request) to prevent returning cached, stale nonce values in subsequent requests.

The client MUST use this new nonce value to create a new Client Attestation PoP JWT and resend the previous request.
The server provided nonce must be included in the `nonce` JWT payload claim, as a JSON string.

The following non-normative example presents an example of a non-success response, including the new nonce in the `Attestation-Nonce` header.
Copy link

Choose a reason for hiding this comment

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

Repeating expression:

Suggested change
The following non-normative example presents an example of a non-success response, including the new nonce in the `Attestation-Nonce` header.
The following non-normative example represents a non-success response, including the new nonce in the `Attestation-Nonce` header.

When receiving a `Attestation-Nonce` in a success response, the client MUST not retry the request.
Instead, the client MUST use the provided nonce the next time a Client Attestation PoP JWT need to be computed.

An example 200 OK response providing a new nonce value is shown below.
Copy link

Choose a reason for hiding this comment

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

Suggested change
An example 200 OK response providing a new nonce value is shown below.
The following non-formative example shows a 200 OK response providing a new nonce value.


## Nonce syntax

The nonce syntax in ABNF as used by {{RFC6749}} (which is the same as the scope-token syntax) is shown below.
Copy link

Choose a reason for hiding this comment

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

I believe this sentence is missing a formative language. We may even add it above where thenonce claim is originally defined.

@paulbastian
Copy link
Collaborator

This is the obvious solution going in the right direction. I'm not convinced yet that this is the ideal solution, especially in combination with DPoP.

Copy link

@peppelinux peppelinux left a comment

Choose a reason for hiding this comment

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

just editorial, consider it approved

@@ -233,6 +234,57 @@ The following example is the decoded header and payload of a JWT meeting the pro
}
~~~

# Authorization Server-provided nonces

This section specifies a mechanism using server provided opaque nonces to limit the lifetime of a Client Attestation PoP JWT, similar to the one defined in section 8 of {{RFC9449}}.

Choose a reason for hiding this comment

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

Suggested change
This section specifies a mechanism using server provided opaque nonces to limit the lifetime of a Client Attestation PoP JWT, similar to the one defined in section 8 of {{RFC9449}}.
This section outlines a method that utilizes server-generated opaque nonces to restrict the lifespan of a Client Attestation Proof of Possession (PoP) JWT., similar to the one defined in section 8 of {{RFC9449}}.

Comment on lines +240 to +241
Without employing such a mechanism, a malicious party controlling the client (potentially including the end-user) can create Client Attestation PoP JWT for use arbitrarily far in the future.
In addition, a mismatch between the time reference of a non-malicious client (e.g. a mobile device) and the time reference of the authorization server, may result in Client Attestation PoP JWT that are never accepted by the authorization server.

Choose a reason for hiding this comment

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

Suggested change
Without employing such a mechanism, a malicious party controlling the client (potentially including the end-user) can create Client Attestation PoP JWT for use arbitrarily far in the future.
In addition, a mismatch between the time reference of a non-malicious client (e.g. a mobile device) and the time reference of the authorization server, may result in Client Attestation PoP JWT that are never accepted by the authorization server.
Without this mechanism, a malicious entity, which could include the end-user, controlling the client can generate Client Attestation Proof of Possession (PoP) JWTs for unrestricted future use. Moreover, a discrepancy between the time reference of a benign client (like a mobile device) and the authorization server's time reference could lead to Client Attestation PoP JWTs that the authorization server never accepts.

Including a nonce value contributed by the authorization server in the Client Attestation PoP JWT MAY be used by authorization servers to limit the lifetime of those proofs, both protecting against attacks and allowing for interoperability with clients with different time references.

An authorization server MAY require the use of nonces.
This requirement is communicated to the client by having the authorization server return an HTTP response with the `400` status and an `error` field with the value `"use_attestation_nonce"`, when the Client Attestation PoP JWT present in the HTTP request does not contain a valid nonce.

Choose a reason for hiding this comment

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

Suggested change
This requirement is communicated to the client by having the authorization server return an HTTP response with the `400` status and an `error` field with the value `"use_attestation_nonce"`, when the Client Attestation PoP JWT present in the HTTP request does not contain a valid nonce.
This requirement is communicated to the Client by having the authorization server return an HTTP response with the `400` status and an `error` field with the value `"use_attestation_nonce"`, when the Client Attestation PoP JWT present in the HTTP request does not contain a valid nonce.

@bc-pi
Copy link
Contributor

bc-pi commented Nov 16, 2023

@paulbastian:
This is the obvious solution going in the right direction. I'm not convinced yet that this is the ideal solution, especially in combination with DPoP.

Agree with the direction and also unsureness around DPoP. There's a lot of overlap and duplicative text and functionality with DPoP in this nonce mechanism and the Client Attestation PoP JWT itself. A lot. It makes me question (again) if it wouldn't be better to use the DPoP Proof JWT as the PoP mechanism in this work rather than recreate so much of it with the Client Attestation PoP JWT.

@tlodderstedt
Copy link
Contributor

@bc-pi
"It makes me question (again) if it wouldn't be better to use the DPoP Proof JWT as the PoP mechanism in this work rather than recreate so much of it with the Client Attestation PoP JWT."

That ties two mechanisms together that were built for different purposes. I think that would make sense if client attestation would always be used with DPoP bound access tokens. That means: a) no client attestation without sender constrained access tokens and b) no mTLS for sender constrained access tokens when using client attestation.

@bc-pi
Copy link
Contributor

bc-pi commented Nov 16, 2023

That ties two mechanisms together that were built for different purposes.

The DPoP proof JWT part of RFC9449 was built exactly for the purpose of proving possession of a key in the context of an HTTP request. And can be used without necessarily binding access tokens.

@pmhsfelix
Copy link
Author

@bc-pi
There's a lot of overlap and duplicative text and functionality with DPoP in this nonce mechanism and the Client Attestation PoP JWT itself. A lot. It makes me question (again) if it wouldn't be better to use the DPoP Proof JWT as the PoP mechanism in this work rather than recreate so much of it with the Client Attestation PoP JWT.

Also had the exact same feeling while writing the PR, namely the creation of a duplicate header and a duplicate OAuth error code. Using DPoP for the proof-of-possession did also cross my mind.

We could perhaps decouple the DPoP proof of possession JWT from the DPoP HTTP header.
My current understanding is that the DPoP header serves two purposes when used on a Token Request:

  • Convey a public key proof-of-possesion JWT.
  • Request the issued tokens to be bound to that public key.
    OTOH, when used on a Resource Server request, the DPoP header seems to only serve the first purpose.

If we want to use DPoP all the way, then this seems to work perfectly.
However, if we don't want to bind the access tokens using DPoP, we could still use the DPoP proof of possession JWT but convey it in a different way (e.g. on the token request body or on a different header, instead of on the DPoP header). The DPoP-nonce and use_dpop_nonce could still be used to provide a fresher nonce.

@bc-pi
Copy link
Contributor

bc-pi commented Nov 16, 2023

[...] if we don't want to bind the access tokens using DPoP, [...]

DPoP itself specifically has text in the DPoP Access Token Request section that allows for an AS to issue access tokens that are not DPoP bound. It says:

An authorization server MAY elect to issue access tokens that are not DPoP bound, which is signaled to the client with a value of Bearer in the token_type parameter of the access token response per [RFC6750].

And this draft could certainly reiterate that, if it went with the DPoP proof as its PoP mechanism.

There is and has been (it's come up previously) a lot of resistance to that approach though. But maybe that resistance was from the misconception that DPoP usage at the token endpoint meant that access tokens had to be DPoP bound.

@tplooker
Copy link
Collaborator

[...] if we don't want to bind the access tokens using DPoP, [...]

DPoP itself specifically has text in the DPoP Access Token Request section that allows for an AS to issue access tokens that are not DPoP bound. It says:

An authorization server MAY elect to issue access tokens that are not DPoP bound, which is signaled to the client with a value of Bearer in the token_type parameter of the access token response per [RFC6750].

And this draft could certainly reiterate that, if it went with the DPoP proof as its PoP mechanism.

There is and has been (it's come up previously) a lot of resistance to that approach though. But maybe that resistance was from the misconception that DPoP usage at the token endpoint meant that access tokens had to be DPoP bound.

At least I'd failed to realise this language existed in DPoP and it feels like because of that I dont really see why we shouldn't consider using the DPoP format for the PoP, it feels both generalised enough for other endpoints AND doesn't mandate the tokens issued from the token endpoint are DPoP bound.

@pmhsfelix
Copy link
Author

So we would use the DPoP token format and convey it via the DPoP header? Or just use the DPoP token format (and associated nonce management)?

@paulbastian
Copy link
Collaborator

I'm somehow hesitant to put everything in the headers.

I think we should evaluate to reuse the key from the client attestation as a key for dpop binding and somehow reuse the nonce providing mechanism. As this is a major use case in mind, we should avoid sending multiple nonces, using multiple keys and proofs.

@ju-cu
Copy link

ju-cu commented Nov 24, 2023

I also prefer reusing and building upon existing specs, i.e. DPoP (RFC 9449) and Assertion based client authentication (RFC 7521), over copying text. This spec already profiles RFC 7521 but could also use DPoP to satisfy the key-binding of the client attestation. Instead of concatenating two JWTs into a single assertion, the client_assertion parameter should hold a key-binding JWT, aka the client attestation JWT. The DPoP HTTP header holds the DPoP proof JWT that serves as the client attestation PoP JWT. In that way, there is no need to define the client attestation PoP JWT in this spec as it can just point to DPoP and use all the mechanisms defined there, including the nonce mechanism. A token request would look like this:

POST /token HTTP/1.1
Host: as.example
Content-Type: application/x-www-form-urlencoded
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImp3ayI6eyJhbGciOiJFUzI1NiIsImNydiI6IlAtMjU2Iiwia3R5IjoiRUMiLCJ4IjoiaThReW03NFRNUHVLQXVKUGlZczFSZlVsYTVjemNxelVobEpmRHNMdzd0NCIsInkiOiJGQjlUY2ZmeVZDSEpFQjJjejc4NTE2MUE0SmxlTkh2cG44bXhHRldZMlNjIn0sImFsZyI6IkVTMjU2In0.eyJqdGkiOiIzNTc2ODI5Ny1kZWM1LTQ2ZjYtODVlNS1iNzU4MjE2YWI1ZmYiLCJodG0iOiJQT1NUIiwiaHR1IjoiaHR0cHM6Ly9hcy5leGFtcGxlL3Rva2VuIiwiaWF0IjoxNzAwODEyODAwLCJub25jZSI6ImV5SjdTX3pHLmV5SkgwLVouSFg0dy03diJ9.5VuDrkd8RhMRaps_AzJBs2p-_UXXWT4dVHITBHiQxe31GeDq81otnIh3HBQN8_XjS1diHPq1tti1pn55eZdI5g

grant_type=authorization_code&
code=n0esc3NRze7LTCu7iYzS6a5acc3f0ogp4&
client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-client-attestation&
client_assertion=eyJhbGciOiJSUzI1NiIsImtpZCI6IjIyIn0.eyJpc3Mi[...omitted for brevity...].cC4hiUPo[...omitted for brevity...]

where the DPoP proof JWT decodes as follows:

JOSE Header
{
  "typ": "dpop+jwt",
  "jwk" : {
    "alg": "ES256",
    "crv": "P-256",
    "kty": "EC",
    "x": "i8Qym74TMPuKAuJPiYs1RfUla5czcqzUhlJfDsLw7t4",
    "y": "FB9TcffyVCHJEB2cz785161A4JleNHvpn8mxGFWY2Sc"
   },
  "alg": "ES256"
}

Payload
{
  "jti": "35768297-dec5-46f6-85e5-b758216ab5ff",
  "htm": "POST",
  "htu": "https://as.example/token",
  "iat": "1700812800",
  "nonce": "eyJ7S_zG.eyJH0-Z.HX4w-7v"
}

Note, that the DPoP proof JWT MUST contain the jwk parameter in the JOSE header according to RFC 9449. In the context of client authentication as defined in this spec, the authorization server MUST use the public key from the cnf claim of the client_assertion to validate the DPoP proof. The authorization server MAY ignore the jwk parameter of the DPoP proof JWT.

@bc-pi
Copy link
Contributor

bc-pi commented Nov 29, 2023

@ju-cu's description of how the DPoP proof JWT would serve as the client attestation PoP JWT is pretty much in line with how I'd envisioned it. Although I believe it'd need to be a bit less prescriptive about checking the confirmation/pop key and just say that the public key from the cnf of Client Attestation JWT must match the key of the jwk header of the DPoP proof JWT. This would allow for the conventional processing order with DPoP where the DPoP proof JWT is validated first followed by binding to or checking the binding of things against the key of the DPoP proof JWT.

@bc-pi bc-pi self-requested a review November 29, 2023 21:20
@pmhsfelix
Copy link
Author

This conversation made me consider the following:

  • There is already RFC 7523 for bearer JWT-based client authentication (as a profile to RFC 7521).
  • However, there isn't any standard for JWT-based client authentication using holder-of-key assertions instead of bearer, which is useful for high assurance scenarios.

So, what if, instead of having an attestation specific specification, this work was divided into two parts.

  • First, a "sibling" of RFC 7523 using DPoP-based holder-of-key JWT assertions.
  • Second, a set of attestation-specific claims, communicating extra attestation-specific information about the client to the AS.

Seems a bit more work, but the result is also much more general: a mechanism for client authentication using JWT assertions with proof-of-possession, independent of attestation. And by using the DPoP specification, the proof-of-possession part could be relatively easier to specify.

If you think this is a conversation worth having, I can create an issue, since this isn't specifically related to this PR.

@tplooker
Copy link
Collaborator

Please see #67 for a concrete proposal to shift to using DPoP as the PoP syntax for this client authentication method.

@peppelinux
Copy link

After giving it much thought, I realized that DPoP uses HTTP headers in HTTP requests, and consequently, it makes sense to obtain a nonce within the headers of the received HTTP responses.

Meanwhile, the nonces we use in OAuth and for endpoint requests utilize GET or POST parameters, and the idea of forcibly using HTTP headers for nonce provisioning doesn't excite me.

We already have a nonce endpoint in IETF literature, for example in ACME (RFC8555), and with the aim of creating something highly specialized, reusable in other contexts, efficient, and scalable, I thought of this:

https://github.com/peppelinux/draft-demarco-nonce-endpoint

@paulbastian
Copy link
Collaborator

We had a discussion on this topic in today's editors call, I posted my thoughts here: #59 (comment)

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.

Using a server provided nonce to limit the lifetime of a Client Attestation PoP JWT
7 participants