Consume internal API on behalf of a citizenΒΆ
This how-to guides you through the steps required to consume an API secured with TokenX:
PrerequisitesΒΆ
- Your application receives requests with a citizen subject token in the
Authorization
header- The subject token can either be from ID-porten or from TokenX itself
- The API you're consuming has granted access to your application
Configure your applicationΒΆ
Enable TokenX in your application:
Depending on how you communicate with the API you're consuming, configure the appropriate outbound access policies.
Exchange tokenΒΆ
To exchange a token, you can either:
- exchange tokens with Texas, or
- exchange tokens manually in your application
Exchange tokens with TexasΒΆ
Texas is not enabled by default
See the Texas documentation for more information.
Send a HTTP POST request to the endpoint found in the NAIS_TOKEN_EXCHANGE_ENDPOINT
environment variable.
The request must have a Content-Type
header set to either:
application/json
orapplication/x-www-form-urlencoded
The body of the request should contain the following parameters:
Parameter | Example Value | Description |
---|---|---|
identity_provider |
tokenx |
Always tokenx . |
target |
<cluster>:<namespace>:<other-app-name> |
The intended audience (target API or recipient) of the new token. |
user_token |
eyJra... |
The user's access token from the inbound request. Token that should be exchanged. |
Your application does not need to validate this token.
Tokens are cached by default with regards to the expires_in
field.
To forcibly fetch a new token, set the skip_cache=true
parameter in the request.
Exchange tokens manuallyΒΆ
Create client assertionΒΆ
To perform a token exchange, your application must authenticate itself. To do so, create a client assertion.
Sign the client assertion with your applications private key contained within the TOKEN_X_PRIVATE_JWK
environment variable.
The assertion must contain the following claims:
Claim | Example Value | Description |
---|---|---|
sub |
dev-gcp:my-team:app-a |
The subject of the token. Set to the TOKEN_X_CLIENT_ID environment variable. |
iss |
dev-gcp:my-team:app-a |
The issuer of the token. Set to the TOKEN_X_CLIENT_ID environment variable. |
aud |
https://tokenx.dev-gcp.nav.cloud.nais.io/token |
The audience of the token. Set to the TOKEN_X_TOKEN_ENDPOINT environment variable. |
jti |
83c580a6-b479-426d-876b-267aa9848e2f |
The JWT ID of the token. Used to uniquely identify a token. Set this to a UUID or similar. |
nbf |
1597783152 |
nbf stands for not before. Set to now. |
iat |
1597783152 |
iat stands for issued at. Set to now. |
exp |
1597783182 |
exp is the expiration time of the token. Between 1 and 120 seconds after now. Typically 30 seconds is fine. |
Additionally, the headers of the assertion must contain the following parameters:
Parameter | Value | Description |
---|---|---|
kid |
93ad09a5-70bc-4858-bd26-5ff4a0c5f73f |
The key identifier of the key used to sign the assertion. This identifier is available in the JWK found in TOKEN_X_PRIVATE_JWK . |
typ |
JWT |
Represents the type of this JWT. Set this to JWT . |
alg |
RS256 |
Represents the cryptographic algorithm used to secure the JWT. Set this to RS256 . |
Example Client Assertion Values
Create and perform exchange requestΒΆ
Now that you have a client assertion, we can use this to exchange the inbound token you received from your consumer.
The token request is an HTTP POST request.
It must have the Content-Type
header set to application/x-www-form-urlencoded
The body of the request should contain the following parameters:
Parameter | Value | Description |
---|---|---|
grant_type |
urn:ietf:params:oauth:grant-type:token-exchange |
Always urn:ietf:params:oauth:grant-type:token-exchange . |
client_assertion_type |
urn:ietf:params:oauth:client-assertion-type:jwt-bearer |
Always urn:ietf:params:oauth:client-assertion-type:jwt-bearer . |
client_assertion |
eyJraWQ... |
The client assertion. Token that authenticates your application. It should be unique and only used once. |
subject_token_type |
urn:ietf:params:oauth:token-type:jwt |
Always urn:ietf:params:oauth:token-type:jwt . |
subject_token |
eyJraWQ... |
The citizen's subject token from the inbound request, either from ID-porten or TokenX. Token that should be exchanged. |
audience |
<cluster>:<namespace>:<appname> |
The identifier for the target application. Intended audience or recipient of the new token. |
Send the request to the token_endpoint
, i.e. the URL found in the TOKEN_X_TOKEN_ENDPOINT
environment variable.
POST ${TOKEN_X_TOKEN_ENDPOINT} HTTP/1.1
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange&
client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&
client_assertion=eY...............&
subject_token_type=urn:ietf:params:oauth:token-type:jwt&
subject_token=eY...............&
audience=prod-gcp:namespace1:app1
Success responseΒΆ
TokenX responds with a JSON object that contains the new access token:
Your application does not need to validate this token.
Cache your tokens
The expires_in
field in the response indicates the lifetime of the token in seconds.
Use this field to cache and reuse the token to minimize network latency impact.
A safe cache key for this flow is key = sha256($subject_token + $audience)
.
Error responseΒΆ
If the exchange request is invalid, you will receive a structured error as specified in RFC 8693, Section 2.2.2:
{
"error_description" : "token exchange audience <some-audience> is invalid",
"error" : "invalid_request"
}
Consume APIΒΆ
Once you have acquired a new token, you can finally consume the target API by using the token as a Bearer token: