News New Look, Same Vision: KrakenD’s Website Redesign

Document updated on Oct 24, 2022

JWT Signing

JWT Signing

The JWT signing component creates a wrapper for your existing login endpoint that signs with your secret key the selected fields of the backend payload right before returning the content to the end-user.

The primary usage for this component is in migrations from monolith to microservices, or in ecosystems where there is no Identity/OAuth server yet, as it allows the immediate adoption of signed JSON Web Tokens without the need to implement a new service.

How does it work

KrakenD relies in your existing login functionality and does all the heavy-lifting of the cryptography so you can focus on validating the user and password.

Your backend needs to implement a login endpoint that after validating the username and password it returns a JSON reponse, and optionally another endpoint for refreshing tokens. For example:

{
    "access_token": {
        "aud": "http://api.example.com",
        "iss": "https://krakend.io",
        "sub": "1234567890qwertyuio",
        "jti": "mnb23vcsrt756yuiomnbvcx98ertyuiop",
        "roles": ["role_a", "role_b"],
        "exp": 1735689600
    },
    "refresh_token": {
        "aud": "http://api.example.com",
        "iss": "https://krakend.io",
        "sub": "1234567890qwertyuio",
        "jti": "mnb23vcsrt756yuiomn12876bvcx98ertyuiop",
        "exp": 1735689600
    },
    "exp": 1735689600
}

The response payload has the structure of a JWT token which contains fields like the subject (a.k.a id_user), the jti (a uniqid for instance), or the expiration of the token to name a few.

When KrakenD receives this JSON payload, it signs the selected group of claims with your secret key. The secret key can be kept in the gateway or URL-downloaded from a trusted machine that you own. With the token signing, you are in control of the private key, and you don’t need to trust an external service to keep it for you.

Example

For instance, your backend could have an endpoint like /token-issuer that when receives the right combination of username and password via POST can identify the user and, instead of setting the session, returns an output like this:

Example 

$curl -X POST --data '{"user":"john","pass":"doe"}' https://your-backend/token-issuer
{
    "access_token": {
        "aud": "https://your.krakend.io",
        "iss": "https://your-backend",
        "sub": "1234567890qwertyuio",
        "jti": "mnb23vcsrt756yuiomnbvcx98ertyuiop",
        "roles": ["role_a", "role_b"],
        "exp": 1735689600
    },
    "refresh_token": {
        "aud": "https://your.krakend.io",
        "iss": "https://your-backend",
        "sub": "1234567890qwertyuio",
        "jti": "mnb23vcsrt756yuiomn12876bvcx98ertyuiop",
        "exp": 1735689600
    },
    "exp": 1735689600
}

Besides these example keys, the payload can contain any other elements you might need.

If you come from a classic login system, based on cookie sessions, you’ll realize that adapting your /login to this output is straightforward. See how to generate a token at the end of the document for more details.

JWT signing settings

The following settings are available to sign JWT:

{
  "endpoints": [
    {
      "endpoint": "/token",
      "method": "POST",
      "extra_config": {
        "auth/signer": {
          "alg": "HS256",
          "jwk_url": "http://your-backend/jwk/symmetric.json",
          "keys_to_sign": [
            "access_token",
            "refresh_token"
          ],
          "kid": "sim2",
          "cipher_suites": [
            5,
            10
          ],
          "jwk_fingerprints": [
            "S3Jha2VuRCBpcyB0aGUgYmVzdCBnYXRld2F5LCBhbmQgeW91IGtub3cgaXQ=="
          ],
          "full": false,
          "disable_jwk_security": false
        }
      }
    }
  ]
}

The example above contains every single option available, but you don’t need them all. See them explained below:

Fields of JWT signer
* required fields

Minimum configuration needs one of: alg + jwk_local_path + disable_jwk_security , or alg + jwk_url

alg *
The hashing algorithm used by the issuer. Usually RS256. The algorithm you choose directly affects the CPU consumption.
Possible values are: "EdDSA" , "HS256" , "HS384" , "HS512" , "RS256" , "RS384" , "RS512" , "ES256" , "ES384" , "ES512" , "PS256" , "PS384" , "PS512"
cipher_suites array
Override the default cipher suites (see JWT validation). Unless you have a legacy JWK, you don’t need to set this value.
Defaults to [49199,49195,49200,49196,52392,52393]
cypher_key string
The cyphering key.
disable_jwk_security boolean
Disables HTTP security of the JWK client and allows insecure connections (plain HTTP) to download the keys. The flag should be false when you use HTTPS, and true when using plain HTTP or loading the key from a local file.
Defaults to false
full boolean
Use JSON format instead of the compact form JWT provides.
Defaults to false
jwk_fingerprints array
A list of fingerprints (the unique identifier of the certificate) for certificate pinning and avoid man in the middle attacks. Add fingerprints in base64 format.
jwk_local_ca string
Path to the CA’s certificate verifying a secure connection when downloading the JWK. Use when not recognized by the system (e.g., self-signed certificates).
jwk_local_path string
Local path to the JWK public keys, has preference over jwk_url. Instead of pointing to an external URL (with jwk_url), public keys are kept locally, in a plain JWK file (security alert!), or encrypted. When encrypted, also add secret_url and cypher_key.
Example: "./jwk.txt"
jwk_url string
The URL to the JWK endpoint with the private keys used to sign the token.
Example: "http://your-backend/jwk/symmetric.json"
keys_to_sign * array
List of all the specific keys that need signing (e.g., refresh_token and access_token).
Examples: "access_token" , "refresh_token"
kid * string
The key ID purpose is to match a specific key, as the jwk_url might contain several keys.
Example: "sim2"
leeway string
A margin of extra time where you will still accept the token after its expiration date. You should not accept expired tokens other than enabling two environments that are not perfectly synchronized and have minor clock drifts to accept each other differences. Any value specified here will be rounded to seconds, with a minimum of one second.
Specify units using ns (nanoseconds), us or µs (microseconds), ms (milliseconds), s (seconds), m (minutes), or h (hours).
Examples: "1m" , "1s"
Defaults to "1s"
secret_url string
An URL with a custom scheme using one of the supported providers (e.g.: awskms://keyID) (see providers).
Examples: "base64key://smGbjm71Nxd1Ig5FS0wj9SlbzAIrnolCz9bQQ6uAhl4=" , "awskms://keyID" , "azurekeyvault://keyID" , "gcpkms://projects/[PROJECT_ID]/locations/[LOCATION]/keyRings/[KEY_RING]/cryptoKeys/[KEY]" , "hashivault://keyID"

Basic JWT signing

Your backend application knows how to issue tokens now, so the gateway can sign them before passing to the user. To achieve that, instead of publishing our internal backend that generates plain tokens under /token-issuer, we only expose via KrakenD a new endpoint named /token (choose your name). This endpoint forwards the data received in the POST (as selected in the example) and returns a signed token when the backend replies.

For instance, from the plain token above we want to sign the keys "access_token" and "refresh_token" so nobody can modify its contents. We need a configuration like this:

{
  "endpoint": "/login",
  "method": "GET",
  "backend": [
    {
      "url_pattern": "/login",
      "host": ["http://backend-url"]
    }
  ],
  "extra_config": {
    "auth/signer": {
      "alg": "HS256",
      "kid": "sim2",
      "keys_to_sign": ["access_token", "refresh_token"],
      "jwk_local_path": "jwk_private_key.json",
      "disable_jwk_security": true
    }
  }
}

The content of jwk_private_key.json used in this example is here.

Notice that we have added a file under jwk_local_path which is a JSON Web key (could also be hosted via jwk_url).

The example adds a disable_jwk_security flag because downloading the file from jwk_local_path does not use the HTTP protocol.

What happens here is that the user requests a /token to the gateway and the issuing is delegated to the backend. The response of the backend with the plain token is signed using your private JWK. And then the user receives the signed token, e.g:

{
    "access_token": "eyJhbGciOiJIUzI1NiIsImtpZCI6InNcdTIifQ.eyJhdWQiOiJodHRwOi8vYXBpLmV4YW1wbGUuY29tIiwiZXhwIjoxNzM1Njg5NjAwLCJpf1MiOiJodHRwczovL2tyYWtlbmQuaW8iLCJqdGkiOiJtbmIyM3Zjf1J0NzU2eXVcd21uYnZjeDk4ZXJ0eXVcd3AiLCJyb2xlcyI6WyJyb2xlX2EiLCJyb2xlX2IiXSwif1ViIjoiMTIzNDU2Nzg5MHF3ZXJ0eXVcdyJ9.htgbhantGcv6zrN1i43Rl58q1sokh3lzuFgzfenI0Rk",
    "exp": 1735689600,
    "refresh_token": "eyJhbGciOiJIUzI1NiIsImtpZCI6InNcdTIifQ.eyJhdWQiOiJodHRwOi8vYXBpLmV4YW1wbGUuY29tIiwiZXhwIjoxNzM1Njg5NjAwLCJpf1MiOiJodHRwczovL2tyYWtlbmQuaW8iLCJqdGkiOiJtbmIyM3Zjf1J0NzU2eXVcd21uMTI4NzZidmN4OThlcnR5dWlvcCIsInN1YiI6IjEyMzQ1Njc4OTBxd2VydHl1aW8ifQ.4v36tuYHe4E9gCVO-_asuXfzSzoJdoR0NJfVQdVKidw"
}

How to convert PEM to JWKS ?

Krakend uses jose library, so if you use your own PEM keys for signing you need to use the following steps to convert your PEM file to JWKS

Notice: if you are using asymmetric algorithms and want to use gateway singing and verification simultaneously, you need to use the following keys with the same kid:

  • private key jwks for signing
  • public key jwks for verification

How to generate a JWT token

Essentially, what you need to adopt JWT in your backend is to adapt your existing /login function (maybe passing an additional ?token=true flag), so when a user logs in, instead of setting the session in a cookie, you return the JSON Web Token for KrakenD to sign.

The token is no more than a JSON output adhering to the JWT standard.

There are a lot of open source libraries to generate JWT tokens in all major languages. Use them or write the JSON output directly with a simple template.

Here is a dummy token for you to check how it looks like.

Live running example

The KrakenD Playground demonstrates how to sign tokens in the /token endpoint and includes an example ready to use. To try it, clone the playground and follow the README.

Scarf

Unresolved issues?

The documentation is only a piece of the help you can get! Whether you are looking for Open Source or Enterprise support, see more support channels that can help you.

See all support channels