OAuth2 - Delegation Token

Token Delegation (a form of extension grant) is when an authorized user/service needs to make futher service calls to downstream dependencies. All the examples below use the asymmetric algorithm RS256 which is means it uses a public and private key pair.

A common flow could be: UI -> BFF -> Resource API

Delegation token

Here the user is authenticated at the UI. This means they have a token that grants them access to do their work. Best practice would be Authorization Code + PKCE. This token is used when the user interacts with the the BFF.

Example JWT (JSON Web Token) generated at jwt.io that could be used between UI and BFF:

1
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2p3dC5pby8iLCJhdWQiOiJiZmYtYXBpIiwiaWF0IjoxNjI1NzY5MzI2LCJleHAiOjE2MjU3Njk5MjYsInVzZXItZnVsbG5hbWUiOiJDYXJsIFBhdG9uIiwidXNlci1pZCI6IjE2ZDMyMTc3LTgxY2YtNDg0OC04NWYzLTYxNTRhZDg0NzJhZSJ9.EYKK4GjD_GokVLcVP5Po1pY8AH6nXiPwG9iDK3-dRUh27XrJ_LW4ZgD-7m0bl0MYIWkqhWZbZFNUTpdhBXtZNBDMQzDaF-Y9S0XhlGfnQiHDM6-f_o0xoB-GYO0K1d9l1Ixi-gwkCIrViJ4hWItxridSdR1hsMaCzTvRv4HVjE6DU1NrpCdD8KDHGTFi55IFTv7PWbQoEabZxZ2CYVfHPSmajv_cfG9BIC_Vm04meKrU2wbWDU9R3vkiwxX00RK_Vmjj90hafe3cbEHJ2aRtkIjXOkBx-r6Zifibhmwf8SENZOdpRCt1fpV1oHd6bLtj_9_dyMiPvVkHB3DD0lebDQ

The decoded payload is

1
2
3
4
5
6
7
8
{
"iss": "https://jwt.io/",
"aud": "bff-api",
"iat": 1625769326,
"exp": 1625769926,
"user-fullname": "Carl Paton",
"user-id": "16d32177-81cf-4848-85f3-6154ad8472ae"
}

The BFF may check any of these claims to validate the token.

The delegation token comes into play when the BFF needs to make a call to the Resource API. The current token claims may not satisfy this request as additional claims or scopes may be required.

Example based on scottbrady91.com

Here we would take the token above and pass it as a form field parameter of &token= along with the scope we need, the example below is &scope=calendar.edit. This assumes the client resource-api has been provisioned to have these scopes and the required additional claims are returned.

1
2
3
4
5
6
7
8
POST /token
Host: auth.example.com
Authorization: Basic cmVzb3VyY2UtYXBpOnNlY3JldA== // base64 encoded string of `resource-api:secret`
Content-Type: application/x-www-form-urlencoded

grant_type=delegation
&scope=calendar.edit
&token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2p3dC5pby8iLCJhdWQiOiJiZmYtYXBpIiwiaWF0IjoxNjI1NzY5MzI2LCJleHAiOjE2MjU3Njk5MjYsInVzZXItZnVsbG5hbWUiOiJDYXJsIFBhdG9uIiwidXNlci1pZCI6IjE2ZDMyMTc3LTgxY2YtNDg0OC04NWYzLTYxNTRhZDg0NzJhZSJ9.EYKK4GjD_GokVLcVP5Po1pY8AH6nXiPwG9iDK3-dRUh27XrJ_LW4ZgD-7m0bl0MYIWkqhWZbZFNUTpdhBXtZNBDMQzDaF-Y9S0XhlGfnQiHDM6-f_o0xoB-GYO0K1d9l1Ixi-gwkCIrViJ4hWItxridSdR1hsMaCzTvRv4HVjE6DU1NrpCdD8KDHGTFi55IFTv7PWbQoEabZxZ2CYVfHPSmajv_cfG9BIC_Vm04meKrU2wbWDU9R3vkiwxX00RK_Vmjj90hafe3cbEHJ2aRtkIjXOkBx-r6Zifibhmwf8SENZOdpRCt1fpV1oHd6bLtj_9_dyMiPvVkHB3DD0lebDQ

The response JWT could be

1
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2p3dC5pby8iLCJhdWQiOiJyZXNvdXJjZS1hcGkiLCJpYXQiOjE2MjU3NjkzMjYsImV4cCI6MTYyNTc2OTkyNiwidXNlci1mdWxsbmFtZSI6IkNhcmwgUGF0b24iLCJ1c2VyLWlkIjoiMTZkMzIxNzctODFjZi00ODQ4LTg1ZjMtNjE1NGFkODQ3MmFlIiwidXNlci1hY2Nlc3MiOiJyZXNvdXJjZS1hcGkiLCJzY29wZSI6WyJjYWxlbmRhci5lZGl0Il19.SW1f7_XVkWE4B_XtjF_Ti6-Z6Zs9XozJz0YIW768NM2n0e2Wf8pHh9jme_dTpwtLAS2_fJt5taVkdYC1d8nK38_aeW6qEYrdaj29FO--D-hJAdxOGSM1gbUwXbuKjawMSMCkwmVO8g9bPJ8ayBx07KURAa0ke8KeGL_GlvsBsInGzJaGC6SuPdkynQu2H0pfaduD0IHVvpVucg1p-fRSg5D7joNIcpxIaHne2SOkz9xNMLNOQuluifQ2Us_hpzc31h5Bn7GJBzMd8ZNFIp8C5AJnqyM5EG7M0suvHkJ-ThXPYO1VgTGVpparfXU547NM-d_W7IY1MFg1gMLWDj2chg

The decoded payload is

1
2
3
4
5
6
7
8
9
10
11
12
{
"iss": "https://jwt.io/",
"aud": "resource-api",
"iat": 1625769326,
"exp": 1625769926,
"user-fullname": "Carl Paton",
"user-id": "16d32177-81cf-4848-85f3-6154ad8472ae",
"user-access": "resource-api",
"scope": [
"calendar.edit"
]
}

This token can then be used to query Resource API with endpoints that require these new claims such as

  • The calendar.edit scope
  • The claim user-access to equal resource-api

These checks would be anything that is sensible to your business requirements. Its would also be best practice to check aud=resource-api.

References