JSON Web Tokens (JWT)
Last updated:
JSON Web Tokens (JWT) are a compact, URL-safe means of representing claims to be transferred between two parties. They are commonly used in API authentication and authorization.
Protecting an API with JWT
To protect an API with JWT, we need to execute the following steps:
- Set Authentication Mode
- Set the JWT Signing Method
- Set the Identity Source and Policy Field Name
- Set a Default Policy
- Generate a JWT
Set Authentication Mode
- Select JSON Web Tokens as the Authentication mode
- Set the cryptographic signing method to
HMAC (shared)
and the public secret astyk123
- Set the Identity Source and Policy Field Name
Set a Default Policy
If Tyk cannot find a pol
claim, it will apply this Default Policy. Select a policy that gives access to this API we are protecting, or go create one first if it doesn’t exist.
Make sure to save the changes to the API Definition.
Generate a JWT
Let’s generate a JWT so we can test our new protected API.
Head on over to https://jwt.io/. Sign the default JWT with our HMAC Shared Secret tyk123
in the VERIFY SIGNATURE section. Your screen should look similar to this:
Copy the Encoded JWT and let’s make a cURL against the Tyk API Definition:
$ curl http://localhost:8080/my-jwt-api/get \
--header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.7u0ls1snw4tPEzd0JTFaf19oXoOvQYtowiHEAZnan74"
Use the JWT
The client includes the JWT in the Authorization header when making requests to the API.
curl -X GET \
https://api.example.com/protected-resource \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
Request:
Parameter | Value |
---|---|
Method | GET |
URL | The API endpoint for the protected resource. |
Authorization | Bearer token, e.g., Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... . |
JWT and Auth0 with Tyk
This will walk you through securing your APIs with JWTs via Auth0. We also have the following video that will walk you through the process.
Prerequisites
- A free account with Auth0
- A Tyk Self-Managed or Cloud installation
Create an Application in Auth0
-
Log in to your Auth0 account.
-
Select APIs from the Applications menu.
-
Click Create API and enter a name and identifier for your API.
-
From the Test tab, follow the instructions on how to get an access token.
-
From the cURL tab, copy the token request command.
curl --request POST \ --url https://dev-yjd8e8u5.us.auth0.com/oauth/token \ --header 'content-type: application/json' \ --data '{"client_id":{CLIENT_ID},"client_secret":{CLIENT_SECRET},"audience":{AUDIENCE},"grant_type":"client_credentials"}'
-
Paste the command in a terminal window to generate your token. Save this token locally.
{ "access_token": "xxxxxxxxxxx", "token_type": "Bearer" }
-
After creating your API, a new Auth0 Application will be created. Go to the Applications section to view it.
-
Copy the Domain from the Basic Information. You will use this when adding an API to Tyk.
Create your API in Tyk
- Log in to your Tyk Dashboard
- Create a new HTTP API (the default http://httpbin.org upstream URL is fine)
- From the Authentication section, select JSON Web Token (JWT) as your authentication mode.
- Select RSA public Key as the JWT signing method.
- Enter your Auth0 Application Domain from Step 8 above to complete the
jwks_uri
end pointhttps://<<your-auth0-domain>>/.well-known/jwks.json
- Copy your
jwks_uri
in to the Public Key field.
- Add an Identity Source and Policy Field Name. The defaults of
sub
andpol
are fine. - Save your API.
- From the System Management section, select Policies
- Click Add Policy
- Select your Auth0 API
- You can keep the rest of the access rights at the defaults.
- Click the Configurations tab and enter a Policy Name and a Keys Expiry after period.
- Click Create Policy.
- Edit your JWT Auth0 API and add the policy you created as the Default Policy from the Authentication section.
- From the top of the API copy the API URL
- From a terminal window using the API URL and the Auth0 generated token.
curl -X GET {API URL} -H "Accept: application/json" -H "Authorization: Bearer {token}"
- If using the httpbin upstream URL as in the example Tyk API, you should see the HTML returned for the httpbin service in your terminal.
- If there is an error with the request, you will see the following error message.
{
"error": "Key not authorized:Unexpected signing method."
}
JWT and Keycloak with Tyk
This guide will walk you through securing your APIs with JWTs via Keycloak.
Prerequisites
- A Keycloak installation
- A Tyk Self-Managed or Cloud installation
Create an Application in Keycloak
-
Access your Keycloak admin dashboard.
-
Navigate to the Administration console.
-
Create a Keycloak realm from the top left-hand side dropdown.
-
Create a Keycloak client.
-
Enter the necessary client details.
-
Enable client authentication and Service account roles under Authentication flow.
-
Set the redirection URL rules.
-
Save.
-
Retrieve the client secret from the Credentials tab under the client you just created.
-
Generate your JWT using
curl
. This is the token you will use to access your services through the Tyk Gateway. You can generate your JWT using either of the following methods. Make sure to replace theKEYCLOAK
prefixed parameters with the appropriate values.Password Grant Type:
curl -L --insecure -s -X POST 'https://KEYCLOAK_URL/realms/KEYCLOAK_REALM/protocol/openid-connect/token' \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "client_id=KEYCLOAK_CLIENT_ID" \ --data-urlencode "grant_type=password" \ --data-urlencode "client_secret=KEYCLOAK_SECRET" \ --data-urlencode "scope=openid" \ --data-urlencode "username=KEYCLOAK_USERNAME" \ --data-urlencode "password=KEYCLOAK_PASSWORD"
Client Credentials Grant Type:
curl -L --insecure -s -X POST 'https://KEYCLOAK_URL/realms/KEYCLOAK_REALM/protocol/openid-connect/token' \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "client_id=KEYCLOAK_CLIENT_ID" \ --data-urlencode "grant_type=client_credentials" \ --data-urlencode "client_secret=KEYCLOAK_SECRET"
A typical response will look something like this:
{ "access_token": "...", "expires_in": 300, "refresh_expires_in": 1800, "refresh_token": "...", "token_type": "Bearer", "id_token": "...", "not-before-policy": 0, "session_state": "...", "scope": "openid profile email" }
Running in k8s
If you are looking to POC this functionality in Kubernetes, you can run a fully worked-out example using our tyk-k8s-demo library. You can read more here.
Create Your JWT API in Tyk
-
Log in to your Tyk Dashboard.
-
Create a new HTTP API (the default
http://httpbin.org
upstream URL is fine). -
Scroll to the Authentication mode section and select JWT from the list.
-
Select RSA public Key as JWT Signing method.
-
Add your JSON Web Key Sets (JWKS) URL in the Public Key box. This can be found through the well-known config endpoint or is typically
https://KEYCLOAK_URL/realms/KEYCLOAK_REALM/protocol/openid-connect/certs
. -
Add an Identity Source and Policy Field Name. The defaults of
sub
andpol
are fine. -
Click on the update button to save the API.
-
Create a policy to manage access to your API.
-
Navigate to the Policies section on the left-hand side menu.
-
Click on Add Policy on the top right-hand side of your screen.
-
Select your API from the Add API Access Rights list.
-
Click on the Configurations tab and choose a policy name and TLL.
-
Add the default policy to the API.
-
Test access to the API using curl.
-
Retrieve the API URL.
-
Test with curl. Make sure to replace
TOKEN
with the JWT you received from the curl earlier.curl 'friendly-slipper-gw.aws-use1.cloud-ara.tyk.io/keycloak.jwt/get' \ -H "Authorization: Bearer TOKEN"
Split Token
OAuth2, OIDC, and their foundation, JWT, have been industry standards for many years and continue to evolve, particularly with the iterative improvements in the OAuth RFC, aligning with FHIR and Open Banking principles. The OAuth flow remains a dominant approach for secure API access.
In the OAuth flow, two types of access tokens are commonly used: opaque and JWT (more precisely, JWS). However, the use of JWTs has sparked debates regarding security, as JWTs can leak information when base64 decoded. While some argue that JWTs should not contain sensitive information, others consider JWTs inherently insecure for authorization.
Introduction to Split Token Flow
JWT Access Tokens can carry sensitive information, making them vulnerable if compromised. The Split Token Flow offers a solution by storing only the JWT signature on the client side while keeping the header and payload on the server side. This approach combines the flexibility of JWTs with the security of opaque tokens, ensuring that sensitive data is not exposed.
How Tyk Implements Split Token Flow
Tyk API Gateway is well-positioned to broker the communication between the client and the authorization server. It can handle requests for new access tokens, split the JWT, and return only the signature to the client, storing the rest of the token internally.
Here’s how you can implement the Split Token Flow using the client credentials flow:
Request a JWT Access Token
$ curl -X POST -H "Content-Type: application/x-www-form-urlencoded" \
https://keycloak-host/auth/realms/tyk/protocol/openid-connect/token \
-d "grant_type=client_credentials" \
-d "client_id=efd952c8-df3a-4cf5-98e6-868133839433" \
-d "client_secret=0ede3532-f042-4120-bece-225e55a4a2d6" -s | jq
This request returns a JWT access token.
Split the JWT
The JWT consists of three parts:
- Header:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
- Payload:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJlbWFpbCI6ImhlbGxvQHdvcmxkLmNvbSJ9
- Signature:
EwIaRgq4go4R2M2z7AADywZ2ToxG4gDMoG4SQ1X3GJ0
Using the Split Token Flow, only the signature is returned to the client, while the header and payload are stored server-side by Tyk.
Create a Virtual Endpoint in Tyk
Create a virtual endpoint or API in Tyk to handle the token request. This endpoint receives the auth request, exchanges credentials with the authorization server, and returns the split token.
Example script for the Virtual Endpoint:
function login(request, session, config) {
var credentials = request.Body.split("&")
.map(function(item, index) {
return item.split("=");
}).reduce(function(p, c) {
p[c[0]] = c[1];
return p;
}, {});
var newRequest = {
"Headers": {"Content-Type": "application/x-www-form-urlencoded"},
"Method": "POST",
"FormData": {
grant_type: credentials.grant_type,
client_id: credentials.client_id,
client_secret: credentials.client_secret
},
"Domain": "https://keycloak-host",
"resource": "/auth/realms/tyk/protocol/openid-connect/token",
};
var response = TykMakeHttpRequest(JSON.stringify(newRequest));
var usableResponse = JSON.parse(response);
if (usableResponse.Code !== 200) {
return TykJsResponse({
Body: usableResponse.Body,
Code: usableResponse.Code
}, session.meta_data)
}
var bodyObj = JSON.parse(usableResponse.Body);
var accessTokenComplete = bodyObj.access_token;
var signature = accessTokenComplete.split(".")[2];
log("completeAccessToken: " + accessTokenComplete);
// Create key inside Tyk
createKeyInsideTyk(signature, bodyObj);
// Override signature
bodyObj.access_token = signature;
delete bodyObj.refresh_expires_in;
delete bodyObj.refresh_token;
delete bodyObj.foo;
var responseObject = {
Body: JSON.stringify(bodyObj),
Code: usableResponse.Code
}
return TykJsResponse(responseObject, session.meta_data);
}
This script handles the login process, splits the JWT, and stores the necessary information in Tyk.
Once the setup is complete, you can test the Split Token Flow by making API calls using the opaque token returned by the virtual endpoint. Tyk will validate the token and reconstruct the full JWT for upstream services.
$ curl localhost:8080/basic-protected-api/get -H "Authorization: MEw….GJ0"
This request uses the opaque token, which Tyk validates and then injects the full JWT into the Authorization header for the API request.
Configure your JWT Setup
Learn how to configure and manage JWT authentication in your Tyk API Gateway.
Set Up JWT Signing Method
Select the cryptographic method to verify JWT signatures from the following options:
- RSA public key
- HMAC shared secret
- ECDSA
- Public JWKS URL
To generate an RSA keypair, use the following commands:
openssl genrsa -out key.rsa
openssl rsa -in key.rsa -pubout > key.rsa.pub
RSA Supported Algorithms
Both RSA & PSA classes of RSA algorithms are supported by Tyk, including:
- RS256
- RS384
- RS512
- PS256
- PS384
- PS512
Read more about the differences between RSA & PSA classes of RSA algorithms here.
To use either - simply select the “RSA” signing method in the Dashboard, and Tyk will use the appropriate algorithm based on the key you provide.
Set Up Individual JWT Secrets
Enable Tyk to validate an inbound token using stored keys:
- Set up your token with the following fields:
"jwt_data": { "secret": "Secret" }
- Ensure the
kid
header field is included in the JWT for validation.- If the
kid
header is missing, Tyk will check thesub
field. This is not recommended but supported.
- If the
The advantage of using RSA is that only the hashed ID and public key of the end user are stored, ensuring high security.
Configure Identity Source and Policy Field Name
Define the identity and policy applied to the JWT:
- Identity Source: Select which identity claim to use (e.g.,
sub
) for rate-limiting and quota counting. - Policy Field Name: Add a policy ID claim to the JWT that applies a specific security policy to the session.
Enable Dynamic Public Key Rotation Using JWKs
Instead of a static public key, configure a public JSON Web Key Sets (JWKs) URL to dynamically verify JWT tokens:
- Use the JWKs URL to dynamically maintain and rotate active public keys.
- Ensure JWTs contain the
kid
header, matching thekid
in the JWK payload for verification.
For example, cURLing the JWKs URL returns:
$ curl http://keycloak_host:8081/auth/realms/master/protocol/openid-connect/certs
{
"keys": [
{
"kid": "St1x2ip3-wzbrvdk4yVa3-inKWdOwbkD3Nj3gpFJwYM",
"kty": "RSA",
"alg": "RS256",
"use": "sig",
"n": "k-gUvKl9-sS1u8odZ5rZdVCGTe...m2bMmw",
"e": "AQAB",
"x5c": [
"MIICmzCCAYMCBgFvyVrRq....K9XQYuuWSV5Tqvc7mzPd/7mUIlZQ="
],
"x5t": "6vqj9AeFBihIS6LjwZhwFLmgJXM",
"x5t#S256": "0iEMk3Dp0XWDITtA1hd0qsQwgES-BTxrz60Vk5MjGeQ"
}
]
}
This is a JWKS complaint payload as it contains the “x5c” entry which contains the public key. Also, the issuer generates the ID Token or Access Token with a header that includes a “kid” that matches the one in the JWKS payload.
Here’s an example of a header belonging to an access token generated by the issuer above.
{
"alg": "RS256",
"typ": "JWT",
"kid": "St1x2ip3-wzbrvdk4yVa3-inKWdOwbkD3Nj3gpFJwYM"
}
The auth (bearer) tokens will be signed by the private key of the issuer, which in this example is our keycloak host. This token can be verified by Tyk using the public key available in the above payload under “x5C”.
All of this happens automatically. You just need to specify to Tyk what the JWKs url is, and then apply a “sub” and default policy in order for everything to work. See Step #3, 4, and 5 under option #1 for explanations and examples.
Adjust JWT Clock Skew Configuration
Prevent token rejection due to clock skew between servers by configuring clock skew values:
jwt_issued_at_validation_skew
jwt_expires_at_validation_skew
jwt_not_before_validation_skew
All values are in seconds. The default is 0
.
Map JWT Scopes to Policies
Assign JWT scopes to security policies to control access:
- Specify scope-to-policy mapping:
"jwt_scope_to_policy_mapping": {
{
"admin": "59672779fa4387000129507d",
"developer": "53222349fa4387004324324e"
},
"jwt_scope_claim_name": "our_scope"
}
"jwt_scope_to_policy_mapping"
provides mapping of scopes (read from claim) to actual policy ID. I.e. in this example we specify that scope “admin” will apply policy"59672779fa4387000129507d"
to a key"jwt_scope_claim_name"
identifies the JWT claim name which contains scopes. This API Spec field is optional with default value"scope"
. This claim value could be any of the following:- a string with space delimited list of values (by standard)
- a slice of strings
- a string with space delimited list of values inside a nested key. In this case, provide
"jwt_scope_claim_name"
in dot notation. For eg."scope1.scope2"
,"scope2"
will be having the list of values nested inside"scope1"
- a slice of strings inside a nested key. In this case, provide
"jwt_scope_claim_name"
in dot notation. For eg."scope1.scope2"
,"scope2"
will be having a slice of strings nested inside"scope1"
- Set the claim name that contains the scopes (default:
scope
):"jwt_scope_claim_name": "our_scope"
Note
Several scopes in JWT claim will lead to have several policies applied to a key. In this case all policies should have "per_api"
set to true
and shouldn’t have the same API ID
in access rights. I.e. if claim with scopes contains value "admin developer"
then two policies "59672779fa4387000129507d"
and "53222349fa4387004324324e"
will be applied to a key (with using our example config above).
Visualize JWT Flow in Tyk API Gateway
View the diagram below for an overview of JWT flow in Tyk: