thephpleague / oauth2-server

A spec compliant, secure by default PHP OAuth 2.0 Server
https://oauth2.thephpleague.com
MIT License
6.53k stars 1.12k forks source link

Add support for RFC8693 - token exchange #1315

Open Radiergummi opened 1 year ago

Radiergummi commented 1 year ago

Token exchange is a new grant type defined by the IETF providing support for impersonation and delegation scenarios, such as the following:

Illustration by Scott Brady Illustration by Scott Brady

In a nutshell, the grant type involves the following steps:

Exchanging a token

This uses a new grant type of urn:ietf:params:oauth:grant-type:token-exchange and allows the requester to identify and authenticate themselves. Here, API1 is swapping the token it received for access to API2.

The subject token is the token sent by the client application, delegated by the user. Since this token was delegated using OAuth, the subject token type is urn:ietf:params:oauth:token-type:access_token (an access token).

POST /token
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&client_id=api1
&client_secret=secret
&scope=api2
&subject_token=accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC
&subject_token_type=urn:ietf:params:oauth:token-type:access_token

Token exchange response

The token response for token exchange is then slightly different than what you are used to:

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-cache, no-store

{
  "access_token": "lt6QCYJ58k44GRoCwwBPcFDPzYzHdGClhM9qCuh39DIL",
  "issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
  "token_type": "Bearer",
  "expires_in": 60
}

The issued_token_type tells API1 (now acting as a client application) that it has sent back an access token. This allows API1 to understand how to use the token it has just received. In this case, it’s an access token that it can use to access a protected resource (API2).

The access token

The initial token was delegated by user, to the client application app, and you’re still acting on their behalf. But, by using the act claim set, you can show that api1 is the current actor. At the very least, this would be valuable for audit trials.

{
  "iss": "https://auth.example.com",
  "sub": "123xyz_user",
  "client_id": "app",
  "aud": "api2",
  "scope": "api2",
  "act": {
    "client_id": "api1"
  },
  "exp": 1443904177,
  "nbf": 1443904077
}

In addition to the act claim type, the Authorized Actor claim type (may_act) allows you to explicitly state who is allowed to act on someone’s behalf. For instance, if you wanted to explicitly state that api1 is authorized to act on app’s behalf, then the original access token API1 receives, may look like:

{
  "iss": "https://auth.example.com",
  "sub": "123xyz_user",
  "client_id": "app",
  "aud": "api1",
  "scope": "api1",
  "may_act": {
    "client_id": "api1"
  },
  "exp": 1443904177,
  "nbf": 1443904077
}

Now, when validating a token exchange request, the authorization server can check that the subject token has this may_act and that it includes the ID of the token exchange requester.

Use Cases

Token Exchange is great for API-to-API delegation, stuff like customer service acting on behalf of a customer, and preserving clear audit trails across services, as well as checking that zero trust checkbox on your compliance terms thoroughly.


I would love to see token exchange being implemented in oauth2-server, and subsequently in Laravel Passport. I have quite a bit of experience working with OAuth in PHP and would like to contribute to this, if there's any interest in implementing that grant type.

marcriemer commented 1 year ago

I'm currently working on the OpenID implementation, which can be helpful to get this done.

Radiergummi commented 1 year ago

In the mean time, I've actually implemented the grant type in our production authorisation server, and it's pretty straight forward.
What I'm unsure about is how to generalise the conditions to issue a token - the exchange grant is meant to be flexible and tightly integrated with application logic; but that could be worked out, I think.

Sephster commented 1 year ago

Definitely interested in an implementation. The problem I have just now is trying to get a new main version out which is unfortunately taking up most of my time but would welcome a PR. I just can't promise it will get merged in too quickly unfortunately as it will definitely miss v9.