I am currently implementing an OAuth token exchange STS and am struggling a bit. The standard is defined in https://www.rfc-editor.org/rfc/rfc8693 My use case involves a chain of system-to-system calls, triggered by a user (principal) action. I want to make sure that authorization on endpoint levels is done by a mix of scopes and white-listed client_ids. In addition I'd like to keep the customer context.
I see that the token exchange scenario is valid here. The specification gives an example where the resulting token contains the actor to whom access was delegated. However, the specification puts the logical system (aka OAuth client) into the "sub" claim of the nested actor information, as shown here:
{
"aud":"https://service26.example.com",
"iss":"https://issuer.example.com",
"exp":1443904100,
"nbf":1443904000,
"sub":"user@example.com",
"act":
{
"sub":"https://service16.example.com",
"act":
{
"sub":"https://service77.example.com"
}
}
}
I find this a bit odd. I would expect the "sub" claim to carry an actual subject, if the actor token was a personalized token as well (say, "adminuser"). In my opinion, the system that was conducting the token exchange would send as an actor token a client_credentials token. This does not contain a "sub" claim, but only the "cid" (client_id) claim. This claim should be included in the "act" claim of the resulting token, so I would expect the resulting token to look like this:
{
"aud":"https://service26.example.com",
"iss":"https://issuer.example.com",
"exp":1443904100,
"nbf":1443904000,
"sub":"user@example.com",
"cid":"3rd_client_that_conducted_token_exchange",
"act":
{
"cid":"2nd_client_id",
"act":
{
"cid":"initial_client_id"
}
}
}
I'm not sure if there is a required structure for the "act" claim (as far as I see, no). Is there a best practice to follow here? Curious for your thoughts! :)
The act
claim does not have a standardised structure, the only thing the RFC mentions is that it should consist of any information that would enable you to identify the actor.
Whether the act
claim should contain information about a user or about a client depends on the architecture of your system, and on which information will be usable or required by your other services. If you have a scenario in which user's privileges are delegated to an admin and you want to have that information in the token, you will put the admin's data in the act.sub
claim. But if you want to point out that one client is delegating privileges to another client, you will have the client id in the act.sub
claim. The client ID can also be the subject, if the delegation is done between clients not users.
Remember that you can also have impersonation scenario for a token exchange. So, in the resulting token you will not have any information about the actor. E.g. you can have your admin impersonate a user. After performing the token exchange you will issue the user's token, without any information that it is actually the admin user who is handling that token.
You mention that you have a chain of systems between which you want to exchange tokens. I believe that for this scenario the client ID will indeed become the subject for your act
claim. If I understand correctly you have a situation in which you have a token for user X, issued to be used at API service 1 (the aud
claim indicates that). Then API service 1 want to gain access to service 2, and needs to exchange the token. It now gets another token where the sub is user X, audience is service 2 and act.sub
is service 1, as this service now acts as the user and wants to access resources at service 2.