Individual JWT Secrets - Key not authorized:signature is invalid

I am new to Tyk and I am trying to create a plugin to check JWS, but I always get the error "Key not authorized:signature is invalid".

Tyk Community version

$ ./tyk --version
v2.9.4

Tyk Configuration

{
  "listen_port": 8080,
  "secret": "352d20ee67be67f6340b4c0605b044b7",
  "template_path": "/opt/tyk-gateway/templates",
  "tyk_js_path": "/opt/tyk-gateway/js/tyk.js",
  "middleware_path": "/opt/tyk-gateway/middleware",
  "use_db_app_configs": false,
  "app_path": "/opt/tyk-gateway/apps/",
  "storage": {
    "type": "redis",
    "host": "redis",
    "port": 6379,
    "username": "",
    "password": "",
    "database": 0,
    "optimisation_max_idle": 2000,
    "optimisation_max_active": 4000
  },
  "enable_analytics": false,
  "analytics_config": {
    "type": "csv",
    "csv_dir": "/tmp",
    "mongo_url": "",
    "mongo_db_name": "",
    "mongo_collection": "",
    "purge_delay": -1,
    "ignored_ips": []
  },
  "health_check": {
    "enable_health_checks": true,
    "health_check_value_timeouts": 60
  },
  "optimisations_use_async_session_write": true,
  "enable_non_transactional_rate_limiter": true,
  "enable_sentinel_rate_limiter": false,
  "enable_redis_rolling_limiter": false,
  "allow_master_keys": false,
  "policies": {
    "policy_source": "file",
    "policy_record_name": "/opt/tyk-gateway/policies/policies.json"
  },
  "hash_keys": true,
  "close_connections": false,
  "http_server_options": {
    "enable_websockets": true
  },
  "allow_insecure_configs": true,
  "coprocess_options": {
    "enable_coprocess": true,
    "coprocess_grpc_server": ""
  },
  "enable_bundle_downloader": true,
  "bundle_base_url": "",
  "global_session_lifetime": 100,
  "force_global_session_lifetime": false,
  "max_idle_connections_per_host": 500
}

Tyk policies

{
    "default": {
        "rate": 1000,
        "per": 1,
        "quota_max": 100,
        "quota_renewal_rate": 60,
        "access_rights": {
            "41433797848f41a558c1573d3e55a410": {
                "api_name": "My API",
                "api_id": "41433797848f41a558c1573d3e55a410",
                "versions": [
                    "Default"
                ]
            }
        },
        "org_id": "54de205930c55e15bd000001",
        "hmac_enabled": false
    },
    "REPLACE_WITH_YOUR_POLICY_ID": {
        "access_rights": {
            "REPLACE_WITH_YOUR_API_ID": {
                "allowed_urls": [],
                "api_id": "REPLACE_WITH_YOUR_API_ID",
                "api_name": "REPLACE WITH YOUR API NAME",
                "versions": [
                    "Default"
                ]
            }
        },
        "org_id": "REPLACE_WITH_YOUR_ORGANIZATION_ID",
        "active": true,
        "name": "REPLACE WITH YOUR POLICY NAME",
        "rate": 100,
        "per": 1,
        "quota_max": 10000,
        "quota_renewal_rate": 3600,
        "hmac_enabled": false,
        "tags": ["token"]
    }
}

Creating the API

$ curl http://localhost:8080/tyk/apis/  \
  -i \
  -s \
  -H "x-tyk-authorization: 352d20ee67be67f6340b4c0605b044b7" \
  -H "Content-Type: application/json" \
  -X POST \
  -d '{
        "name": "REPLACE WITH YOUR API NAME",
        "slug": "httpbin.org",
        "api_id": "REPLACE_WITH_YOUR_API_ID",
        "org_id": "REPLACE_WITH_YOUR_ORGANIZATION_ID",
        "auth": {
          "auth_header_name": "Approov-Token"
        },
        "definition": {
          "location": "header",
          "key": "x-api-version"
        },
        "version_data": {
          "not_versioned": true,
          "versions": {
            "Default": {
              "name": "Default",
              "use_extended_paths": true
            }
          }
        },
        "proxy": {
          "listen_path": "/httpbin.org/",
          "target_url": "https://httpbin.org/",
          "strip_listen_path": true
        },
        "active": true,
        "enable_jwt": true,
        "jwt_signing_method": "hmac",
        "jwt_default_policies": [
          "REPLACE_WITH_YOUR_POLICY_ID"
        ]
    }'
HTTP/1.1 200 OK
Content-Type: application/json
Date: Tue, 14 Apr 2020 17:53:16 GMT
Content-Length: 66

{"key":"REPLACE_WITH_YOUR_API_ID","status":"ok","action":"added"}

Creating the Key

First the secret to sign the JWS tokens

$ openssl rand -base64 64 | tr -d '\n'; echo
rZ0G0fl2EVlpKtbS465OmK3QrClCROoNpDqTlefxDhfON6QW7c0KSog1gW4vJjL4TD01Nkehafg2x2jkVrEY2A==

The key

curl http://localhost:8080/tyk/keys/create \
  -i \
  -s \
  -H "x-tyk-authorization: 352d20ee67be67f6340b4c0605b044b7" \
  -H "Content-Type: application/json" \
  -X POST \
  -d '{
    "allowance": 1000,
    "rate": 1000,
    "per": 1,
    "expires": -1,
    "quota_max": -1,
    "org_id": "REPLACE_WITH_YOUR_ORGANIZATION_ID",
    "quota_renews": 1449051461,
    "quota_remaining": -1,
    "quota_renewal_rate": 60,
    "access_rights": {
      "REPLACE_WITH_YOUR_API_ID": {
        "api_id": "REPLACE_WITH_YOUR_API_ID",
        "api_name": "REPLACE WITH YOUR API NAME",
        "versions": ["Default"]
      }
    },
    "meta_data": {},
    "jwt_data": {
      "secret": "rZ0G0fl2EVlpKtbS465OmK3QrClCROoNpDqTlefxDhfON6QW7c0KSog1gW4vJjL4TD01Nkehafg2x2jkVrEY2A=="
    },
    "apply_policy_id": "REPLACE_WITH_YOUR_POLICY_ID"
  }'
HTTP/1.1 200 OK
Content-Type: application/json
Date: Tue, 14 Apr 2020 18:02:52 GMT
Content-Length: 129

{"key":"REPLACE_WITH_YOUR_ORGANIZATION_ID5a48781d19de40dd8e0abe5a63dd9417","status":"ok","action":"added","key_hash":"f787223f"}

Tyk reload

curl http://localhost:8080/tyk/reload/group \
  -i \
  -H "x-tyk-authorization: 352d20ee67be67f6340b4c0605b044b7"
HTTP/1.1 200 OK
Content-Type: application/json
Date: Tue, 14 Apr 2020 18:05:48 GMT
Content-Length: 29

{"status":"ok","message":""}

API request

$ curl -i http://localhost:8080/httpbin.org/uuid \
    -H "Approov-Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IlJFUExBQ0VfV0lUSF9ZT1VSX09SR0FOSVpBVElPTl9JRDVhNDg3ODFkMTlkZTQwZGQ4ZTBhYmU1YTYzZGQ5NDE3In0.eyJleHAiOjM3MDg2ODMyMDV9.PA39C-_TnqdZ3ebJds9rz71H1FjjtYl8dCohgMwZkkQ" \
    -H "Content-Type: application/json"
HTTP/1.1 403 Forbidden
Content-Type: application/json
X-Generator: tyk.io
Date: Tue, 14 Apr 2020 18:08:59 GMT
Content-Length: 58

{
    "error": "Key not authorized:signature is invalid"
}

The token above was generated at jwt.io with the base64 secret I generated above with openssl, therefore with a tick in the box for secret base64 encoded.

Now If I un-tick the box for secret base64 encoded I get another JWS, and if I use it in another request:

$ curl -i http://localhost:8080/httpbin.org/uuid \
    -H "Approov-Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IlJFUExBQ0VfV0lUSF9ZT1VSX09SR0FOSVpBVElPTl9JRDVhNDg3ODFkMTlkZTQwZGQ4ZTBhYmU1YTYzZGQ5NDE3In0.eyJleHAiOjM3MDg2ODMyMDV9.yzRJI5oSClCNNpZg7UDI3RwOCqB1nR0vqIdpwmNyvdw" \
    -H "Content-Type: application/json"
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Content-Length: 53
Content-Type: application/json
Date: Tue, 14 Apr 2020 18:14:58 GMT
Server: gunicorn/19.9.0
X-Ratelimit-Limit: 10000
X-Ratelimit-Remaining: 9999
X-Ratelimit-Reset: 1586891697

{
  "uuid": "e53dc13d-bd0c-4807-b32c-067c1adad702"
}

For my surprise I get a successful response, but I am not understanding how is that possible? What am I missing?

To note that I tried to create JWS with CLI tools and the result is always an invalid JWS when the secret is base64 encoded, even if I switch to use a base64url encoded secret I still get the validation error.

Hi!

When you specify secret in KID, Tyk do not really support automatically decoding base64 secrets. So you need to specify the ‘real’ secret used to sign the token. E.g. you should not base64 encode secret, for using it in Tyk.

Does it makes sense?

From docs:

"enable_jwt": true,
"jwt_source": "BASE64-Encoded secret",
...

and:

"jwt_data": {
  "secret": "Secret"
}

Doesn’t in the sense the docs are not clear, and not consistent :wink:

All libraries I have worked until now for validating JWT’s support by default Base64 encoded secrets, thus I am curious to know the reason why TYK is supporting them in on part of the configuration and not in the other?

Thanks for your help :slight_smile:

All libraries I have worked until now for validating JWT’s support by default Base64 encoded secrets, thus I am curious to know the reason why TYK is supporting them in on part of the configuration and not in the other?

That’s a big mystery for me as well :slight_smile:

1 Like

Thanks for the quick reply.


Regarding the secret I have to say that not supporting base64 encoded secrets weakens the security of the JWS token signature, because now the secret used to sign the JWS needs to have printable characters only, while with base64 encoded secrets the secret can contain non printable characters.

So not supporting base64 encode secrets to allow for a secret to be encoded from it’s binary form, means loosing around 25% of it’s entropy, therefore more vulnerable to brute force attacks.