JWT (JSON Web Token) authentication mode for APIs, and JWT API keys

Hello all,

For the “JSON Web Token” API authentication mode, as far as I understand from the documentation, access control can only be enforced with policies (by looking at the Policy Field Name). How does this relate to API Keys? Will access control still be performed with API keys or only via policies?

Also, for API Key creation, in the dashboard under create a key there is the option ‘This is a JWT ID’
The explanation is not really clear on how this works and if it’s related to the API authentication mode above.

Thank you

1 Like

Hi - hopefully this will explain. //tyk.io/docs/basic-config-and-security/security/security-policies/

(Keys are still used, but a policy is like a “template” for keys)

Let me know if that explains it, or if you need further info?

Hi @James, thanks for the help.

I still didn’t get what a “This is a JWT ID” under the API Key creation does, can you explain better? Because I created a new API Key with this setting enabled and at the end the generated key was not a JWT one… It was just a regular hash. How is this meant to be used?

Also, using the “JSON Web Token” API authentication mode, the “Identity Source” (sub) apparently doesn’t matter… If I send a token with a valid sub, it ask me for a valid policy. If I put a valid policy (pol) in the JWT token, it allows me even if the sub is invalid.


Yes the sub field is arbitrary. This is traditionally used as a user ID of some sort, and is converter into an internal representation of that user which is used to track rate limits and quotas of future calls with that ID. A valid policy is required to determine the access and rate/quota limits for that user.

For the token you created, this is for the following option: //tyk.io/docs/basic-config-and-security/security/authentication-authorization/json-web-tokens/
The JWT ID that you created a token ID that is bound to a JWT key so that inbound tokens that bear this token can be validated.

Let me know if you need more information!

Hi @Luan

Thanks for the answer, i think that part of the documentation is a bit unclear.
Could you explain when creating an API key from the dashboard, how does the option

x This is a JWT ID

affect the generation of this api key?

Could you also elaborate what you mean by: “The JWT ID that you created a token ID that is bound to a JWT key so that inbound tokens that bear this token can be validated.” it’s not really clear for me

When a JWT is validated, a token is created to store track the session data for that JWT. This is tied to the “sub” claim in the token.

In the case where we want each key to be signed individually, e.g. different HMAC secrets for each key, we need a way to store the individual secrets for the gateway to be able to verify the tokens accurately. For this purpose we create an API key with the “This is a JWT ID” checkbox, which allows us to store the individual secret for that JWT in the session for that key. The key we created is then passed as the “kid” header in the JWT, allowing the secret to be retrieved from the session to verify the JWT with.

In this case, the created key is also used to track the user identity/quota/rate limits so the “sub” claim is not read

Hope that helps!


The key we created is then passed as the “kid” header in the JWT, allowing the secret to be retrieved from the session to verify the JWT with

This is the JWT passed to the api gateway right?
So this JWT has to contain:

  • a kid header/sub field with the key id
  • a pol/policy field field to validate ratelimits etc…

The “kid” header is only needed if you want each JWT to be encrypted with it’s own secret as mentioned here:

To encrypt the each JWT with the same secret (standard behaviour), all that is needed is a user-specific name or ID in the “sub” claim, and a policy to apply to the JWT in “pol”. No need to manually create any API keys.

Add the secret to the API definition, and add the JWT in a header in the API call to the gateway:
Authorization: Bearer {token}

Hi @Luan Thanks for the reply.

I need to create my own keys that’s why i was asking about the each individual key.

However i’ve noticed something strange.

I’ve created an api key with an individual secret using this body:
POST: http://www.tyk-test.com:8181/tyk/keys/aaa-bbb-999

        "allowance": 1000,
        "rate": 1000,
        "per": 60,
        "expires": -1,
        "quota_max": -1,
        "quota_renews": 1449051461,
        "quota_remaining": -1,
        "quota_renewal_rate": 60,
        "access_rights": {
            "a8de59c9cd404abc46f07a841a7aeb6a": {
                "api_name": "Test with api key",
                "api_id": "a8de59c9cd404abc46f07a841a7aeb6a",
                "versions": [
                "allowed_urls": []
        "org_id": "5aaa35ad7d0d6e000187d6ea",
        "oauth_client_id": "",
        "basic_auth_data": {
            "password": "",
            "hash_type": ""
        "jwt_data": {
            "secret": "xxx"
        "hmac_enabled": false,
        "hmac_string": "",
        "is_inactive": false,
        "apply_policy_id": "",
        "apply_policies": [],
        "data_expires": 0,
        "monitor": {
            "trigger_limits": null
        "meta_data": {
        	"aaa": 1
        "tags": ["aaa"]

And generated a token with this header:

  "alg": "HS256",
  "typ": "JWT",
  "kid": "aaa-bbb-999"

Then used this JWT to get access to the API. This is all good.
However chaning the kid in the header to “aaa-bbb-99” and regenerating the JWT I still get access. How is this possible?

I’ve noticed that behaviour with a regular access token as well. Is this a vulnerability in Tyk gateway?

1 Like

Hi @ionutzp

What data have you got in your sub claim in the body of your JWT?

If Tyk can’t find the token ID in the header it will search for this field.

Can you go into more detail on this?

Hello @Martin,

I also changed the value of the sub by removing a digit. It seems that if I change more characters in the sub or kid I don’t get access but if I change only 1 character I get access even if the token is different. I will try it again to confirm.

1 Like

We are aware of this and it is an issue with our hash implementation - unfortunately fixing it breaks backwards compatibility, so we’re looking for alternative ways to approach this.

However, with a JWT, the token itself is still being cryptographically validated each time, so in order for a user to gain access, they would need to generate a valid signed token from the user’s private key, changing the kid or the sub wouldn’t affect that element of how Tyk evaluates access control.

@Martin @Luan thank you for your help so far. I’ve experimented further with the Tyk gateway and there is still something unclear to me.

I’ve created an API key holding a secret via the gateway api:

POST tyk/keys/key-id-random-999

 "jwt_data": {
       "secret": "secret-99"

and i’ve included it’s id in the kid of the JWT i send to the api for authorization. The JWT was successfully parsed by the gateway which validated it and let the request go through. Also tested that the ratelimit is enforced on this api key.

One thing i’ve noticed that in the dashboard key usage section (/#/activity-key) a new key is generated and the key used in the JWT is not present. I can delete it but i cannot see it in the dashboard. This new key automagically created key cannot be deleted (even if the delete request says it is deleted) and cannot be retrieved - errors with “Attempting to access a non owned key”.

I’m a bit confused about what the correct flow and use case is here and what is the difference between the 2 keys (the one i created holding the JWT secret and the one generated by Tyk)


Could you confirm that it is in fact a new key and not simply the hash of the key you created? I am unable to reproduce this at the moment. By default, only a hash of the key is shown in the Dashboard analytics.

Could you provide some screenshots of the problem or steps to reproduce?



I’ve checked what you suggested and you are indeed right. I disabled keyhashing and it is the same key in the analytics that was used in the JWT. I also now set the alias in the key so i can see it in the dashboard.