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¶
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: