Skip to content

Commit 417135f

Browse files
committed
initial pass add DPoP support for ID-JAG
1 parent 3bce7fe commit 417135f

File tree

1 file changed

+150
-0
lines changed

1 file changed

+150
-0
lines changed

draft-ietf-oauth-identity-assertion-authz-grant.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ normative:
4747
RFC8693:
4848
RFC8707:
4949
RFC8725:
50+
RFC7800:
51+
RFC9449:
5052
I-D.ietf-oauth-identity-chaining:
5153
IANA.media-types:
5254
IANA.oauth-parameters:
@@ -79,6 +81,7 @@ informative:
7981
RFC9470:
8082
RFC9728:
8183
I-D.ietf-oauth-client-id-metadata-document:
84+
I-D.parecki-oauth-jwt-dpop-grant:
8285

8386
--- abstract
8487

@@ -543,6 +546,153 @@ TBD: It may make more sense to request the Identity Assertion JWT Authorization
543546

544547
This specification is intended for cross-domain uses where the Client, Resource App, and Identity Provider are all in different trust domains. In particular, the Identity Provider MUST NOT issue access tokens in response to an ID-JAG it issued itself. Doing so could lead to unintentional broadening of the scope of authorization.
545548

549+
## Sender Constraining Tokens
550+
551+
### Proof-of-Possession
552+
553+
Identity Assertion JWT Authorization Grant may support key binding to enable sender-constrained tokens as described in {{Section 4 of I-D.ietf-oauth-identity-chaining}} and {{I-D.parecki-oauth-jwt-dpop-grant}}. This provides additional security by binding tokens to a specific cryptographic key, preventing reuse by parties that do not have access to the private key.
554+
555+
Proof-of-possession is demonstrated by the client presenting a DPoP proof JWT (as defined in {{RFC9449}}) in a `DPoP` HTTP header. The DPoP proof demonstrates that the client possesses the private key corresponding to a public key. This public key can be bound to tokens, ensuring that only the holder of the private key can use those tokens.
556+
557+
The `cnf` (confirmation) claim, as defined in {{RFC7800}}, is used to bind a public key to a JWT. When an ID-JAG contains a `cnf` claim with a `jwk` property, it indicates that the ID-JAG is bound to that specific public key, and proof of possession of the corresponding private key MUST be demonstrated when using the ID-JAG.
558+
559+
The following sections describe the processing rules for proof-of-possession at two stages: during the Token Exchange (when requesting an ID-JAG from the IdP) and during the ID-JAG exchange (when exchanging the ID-JAG for an access token at the Resource Authorization Server).
560+
561+
#### Proof-of-Possession During Token Exchange
562+
563+
When a client requests an ID-JAG from the IdP Authorization Server via Token Exchange, the client MAY include a DPoP proof in the request. This demonstrates possession of a key that can be bound to the ID-JAG.
564+
565+
The client generates a key pair and includes a DPoP proof JWT in the `DPoP` header of the Token Exchange request:
566+
567+
POST /oauth2/token HTTP/1.1
568+
Host: acme.idp.example
569+
Content-Type: application/x-www-form-urlencoded
570+
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDI...
571+
572+
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
573+
&requested_token_type=urn:ietf:params:oauth:token-type:id-jag
574+
&audience=https://acme.chat.example/
575+
&resource=https://api.chat.example/
576+
&scope=chat.read+chat.history
577+
&subject_token=eyJraWQiOiJzMTZ0cVNtODhwREo4VGZCXzdrSEtQ...
578+
&subject_token_type=urn:ietf:params:oauth:token-type:id_token
579+
580+
The IdP Authorization Server processes the request as follows:
581+
582+
1. If a DPoP proof is present, the IdP MUST validate it according to {{Section 4 of RFC9449}}. The `htm` claim MUST be `POST`, and the `htu` claim MUST match the token endpoint URL.
583+
584+
2. If the DPoP proof is valid, the IdP MAY include a `cnf` claim in the issued ID-JAG containing the public key from the DPoP proof's `jwk` header parameter. The `cnf` claim format follows {{RFC7800}}:
585+
586+
{
587+
"jti": "9e43f81b64a33f20116179",
588+
"iss": "https://acme.idp.example",
589+
"sub": "U019488227",
590+
"aud": "https://acme.chat.example/",
591+
"client_id": "f53f191f9311af35",
592+
"exp": 1311281970,
593+
"iat": 1311280970,
594+
"resource": "https://api.chat.example/",
595+
"scope": "chat.read chat.history",
596+
"cnf": {
597+
"jwk": {
598+
"kty": "EC",
599+
"crv": "P-256",
600+
"x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU",
601+
"y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0"
602+
}
603+
}
604+
}
605+
606+
3. The token exchange response does not indicate whether key binding was performed (since `token_type` is fixed to `N_A`). The client must inspect the ID-JAG to determine if a `cnf` claim is present.
607+
608+
4. If no DPoP proof is presented, the IdP issues an ID-JAG without a `cnf` claim.
609+
610+
#### Proof-of-Possession During ID-JAG Exchange
611+
612+
When a client exchanges an ID-JAG for an access token at the Resource Authorization Server, the processing rules depend on whether the ID-JAG contains a `cnf` claim and whether the client presents a DPoP proof.
613+
614+
##### ID-JAG Contains `cnf` Claim and DPoP Proof is Presented
615+
616+
If the ID-JAG contains a `cnf` claim and the client presents a DPoP proof, the Resource Authorization Server MUST:
617+
618+
1. Validate the DPoP proof according to {{Section 4 of RFC9449}}.
619+
620+
2. Extract the public key from the `jwk` header parameter of the DPoP proof.
621+
622+
3. Extract the public key from the `jwk` property of the `cnf` claim in the ID-JAG.
623+
624+
4. Compare the two public keys. They MUST match exactly. If they do not match, the request MUST fail with an `invalid_grant` error.
625+
626+
5. If the keys match, the Resource Authorization Server MAY issue a sender-constrained access token (e.g., a DPoP-bound token) per the Resource Server configuration. The issued access token SHOULD be bound to the same key.
627+
628+
Example request:
629+
630+
POST /oauth2/token HTTP/1.1
631+
Host: acme.chat.example
632+
Content-Type: application/x-www-form-urlencoded
633+
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDI...
634+
635+
grant_type=urn:ietf:params:oauth:grant-type:jwt-dpop
636+
&assertion=eyJhbGciOiJIUzI1NiIsInR5cCI6Im9hdXRoLWlkLWphZytqd3QifQ...
637+
638+
Example successful response with DPoP-bound token:
639+
640+
HTTP/1.1 200 OK
641+
Content-Type: application/json;charset=UTF-8
642+
Cache-Control: no-store
643+
Pragma: no-cache
644+
645+
{
646+
"token_type": "DPoP",
647+
"access_token": "2YotnFZFEjr1zCsicMWpAA",
648+
"expires_in": 86400,
649+
"scope": "chat.read chat.history"
650+
}
651+
652+
##### ID-JAG Contains `cnf` Claim but DPoP Proof is Not Presented
653+
654+
If the ID-JAG contains a `cnf` claim but the client does not present a DPoP proof, the Resource Authorization Server MUST reject the request with an `invalid_grant` error, as the ID-JAG requires proof of possession.
655+
656+
Example error response:
657+
658+
HTTP/1.1 400 Bad Request
659+
Content-Type: application/json
660+
Cache-Control: no-store
661+
662+
{
663+
"error": "invalid_grant",
664+
"error_description": "Proof of possession required for this authorization grant"
665+
}
666+
667+
##### ID-JAG Does Not Contain `cnf` Claim and DPoP Proof is Presented
668+
669+
If the ID-JAG does not contain a `cnf` claim but the client presents a DPoP proof, the Resource Authorization Server:
670+
671+
1. MUST validate the DPoP proof according to {{Section 4 of RFC9449}}.
672+
673+
2. MAY issue a sender-constrained access token (e.g., a DPoP-bound token) per the Resource Server configuration at the Authorization Server, binding the access token to the key demonstrated in the DPoP proof.
674+
675+
3. The access token response will indicate the token type (e.g., `DPoP` for DPoP-bound tokens, or `Bearer` for unconstrained tokens).
676+
677+
##### ID-JAG Does Not Contain `cnf` Claim and DPoP Proof is Not Presented
678+
679+
If the ID-JAG does not contain a `cnf` claim and the client does not present a DPoP proof:
680+
681+
1. The Resource Authorization Server MAY issue an unconstrained Bearer token.
682+
683+
2. However, if the Resource Server configuration at the Authorization Server requires constrained tokens for that Resource Server, the request MUST fail with an `invalid_grant` error.
684+
685+
Example error response when constrained tokens are required:
686+
687+
HTTP/1.1 400 Bad Request
688+
Content-Type: application/json
689+
Cache-Control: no-store
690+
691+
{
692+
"error": "invalid_grant",
693+
"error_description": "Sender-constrained tokens required for this resource server"
694+
}
695+
546696

547697
# IANA Considerations
548698

0 commit comments

Comments
 (0)