Skip to main content

Overview

This endpoint demonstrates KrakenD’s JWT signing capability. It fetches token data from a backend service and signs the tokens using HMAC-SHA256 before returning them to the client. HTTP Method: POST
Endpoint: /token

What It Demonstrates

  • JWT Signing: Signs JWTs using the auth/signer component
  • Token Generation: Creates access and refresh tokens
  • HMAC Algorithm: Uses HS256 for symmetric signing
  • Key Management: References local JWK files for signing keys

Request Example

curl -X POST http://localhost:8080/token

Expected Response

{
  "access_token": "eyJhbGciOiJIUzI1NiIsImtpZCI6InNpbTIiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOlsiaHR0cDovL2FwaS5leGFtcGxlLmNvbSJdLCJleHAiOjE3MDk0ODUyMDAsImlhdCI6MTcwOTQ4MTYwMCwiaXNzIjoiaHR0cHM6Ly9rcmFrZW5kLmlvIiwianRpIjoidG9rZW4xMjMiLCJyb2xlcyI6WyJyb2xlX2EiLCJyb2xlX2IiLCJyb2xlX2MiXSwic3ViIjoidXNlcjEyMyJ9.SIGNATURE_HERE",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsImtpZCI6InNpbTIiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOlsiaHR0cDovL2FwaS5leGFtcGxlLmNvbSJdLCJleHAiOjE3MDk1NjgwMDAsImlhdCI6MTcwOTQ4MTYwMCwiaXNzIjoiaHR0cHM6Ly9rcmFrZW5kLmlvIiwianRpIjoicmVmcmVzaDEyMyIsInN1YiI6InVzZXIxMjMifQ.SIGNATURE_HERE",
  "token_type": "Bearer",
  "expires_in": 3600
}

Token Payload

When decoded, the access token contains:
{
  "alg": "HS256",
  "kid": "sim2",
  "typ": "JWT"
}

Payload (Claims)

{
  "aud": ["http://api.example.com"],
  "exp": 1709485200,
  "iat": 1709481600,
  "iss": "https://krakend.io",
  "jti": "token123",
  "roles": ["role_a", "role_b", "role_c"],
  "sub": "user123"
}
Claims:
  • aud - Audience (who the token is intended for)
  • exp - Expiration time (Unix timestamp)
  • iat - Issued at (Unix timestamp)
  • iss - Issuer (who created the token)
  • jti - JWT ID (unique token identifier)
  • roles - User roles (custom claim)
  • sub - Subject (user identifier)

Backend Service

  • Host: http://fake_api (inherited from global config)
  • URL Pattern: /token.json
  • Purpose: Returns unsigned token data that KrakenD will sign

Backend Response Format

The backend returns token data in this format:
{
  "access_token": {
    "aud": ["http://api.example.com"],
    "exp": 1709485200,
    "iat": 1709481600,
    "iss": "https://krakend.io",
    "jti": "token123",
    "roles": ["role_a", "role_b", "role_c"],
    "sub": "user123"
  },
  "refresh_token": {
    "aud": ["http://api.example.com"],
    "exp": 1709568000,
    "iat": 1709481600,
    "iss": "https://krakend.io",
    "jti": "refresh123",
    "sub": "user123"
  },
  "token_type": "Bearer",
  "expires_in": 3600
}
KrakenD signs the access_token and refresh_token objects and converts them to JWT strings.

KrakenD Configuration

{
  "@comment": "Feature: JWT signing, as described at https://www.krakend.io/docs/authorization/jwt-signing/",
  "endpoint": "/token",
  "backend": [
    {
      "url_pattern": "/token.json"
    }
  ],
  "extra_config": {
    "auth/signer": {
      "alg": "HS256",
      "kid": "sim2",
      "keys_to_sign": ["access_token", "refresh_token"],
      "jwk_local_path": "/opt/krakend/jwk-symmetric.json",
      "disable_jwk_security": true
    }
  }
}

Key Configuration Options

auth/signer

Configures JWT signing behavior:
{
  "auth/signer": {
    "alg": "HS256",
    "kid": "sim2",
    "keys_to_sign": ["access_token", "refresh_token"],
    "jwk_local_path": "/opt/krakend/jwk-symmetric.json",
    "disable_jwk_security": true
  }
}

Algorithm (alg)

"alg": "HS256"
The signing algorithm:
  • HS256 - HMAC with SHA-256 (symmetric key)
  • Other options: HS384, HS512, RS256, RS384, RS512, ES256, etc.

Key ID (kid)

"kid": "sim2"
Identifies which key from the JWK file to use for signing. This value appears in the JWT header.

Keys to Sign (keys_to_sign)

"keys_to_sign": ["access_token", "refresh_token"]
Specifies which fields in the backend response should be signed as JWTs. KrakenD:
  1. Extracts the object from each field
  2. Signs it as a JWT
  3. Replaces the object with the JWT string

JWK Local Path (jwk_local_path)

"jwk_local_path": "/opt/krakend/jwk-symmetric.json"
Path to the local JWK file containing signing keys. Example JWK File:
{
  "keys": [
    {
      "kty": "oct",
      "kid": "sim2",
      "k": "base64_encoded_secret_key",
      "alg": "HS256"
    }
  ]
}

Disable JWK Security

"disable_jwk_security": true
⚠️ Development Only: Disables JWK security checks. In production:
  • Remove this option or set to false
  • Use proper key management
  • Rotate keys regularly
  • Protect JWK files with appropriate permissions

Using the Token

Once you receive the token, use it to access protected endpoints:
# Get token
TOKEN=$(curl -s -X POST http://localhost:8080/token | jq -r '.access_token')

# Use token to access protected endpoint
curl -H "Authorization: Bearer $TOKEN" \
  http://localhost:8080/private/custom

Token Lifecycle

1. Generate Token

curl -X POST http://localhost:8080/token

2. Use Access Token

curl -H "Authorization: Bearer <access_token>" \
  http://localhost:8080/private/custom

3. Token Expires

After expires_in seconds (3600 = 1 hour), the access token expires.

4. Refresh Token

Use the refresh token to obtain a new access token (requires additional endpoint configuration).

Supported Algorithms

Symmetric (HMAC)

  • HS256 - HMAC with SHA-256
  • HS384 - HMAC with SHA-384
  • HS512 - HMAC with SHA-512
Use Case: Internal services, API-to-API communication

Asymmetric (RSA)

  • RS256 - RSA with SHA-256
  • RS384 - RSA with SHA-384
  • RS512 - RSA with SHA-512
Use Case: Public clients, distributed systems, key rotation

Asymmetric (ECDSA)

  • ES256 - ECDSA with P-256 and SHA-256
  • ES384 - ECDSA with P-384 and SHA-384
  • ES512 - ECDSA with P-521 and SHA-512
Use Case: High security, smaller signatures

Security Best Practices

1. Use Strong Secrets

For HS256, use a strong random secret (at least 256 bits):
openssl rand -base64 32

2. Rotate Keys Regularly

Update the JWK file periodically and use the kid to support multiple keys during rotation.

3. Set Appropriate Expiration

  • Access tokens: Short-lived (15 minutes - 1 hour)
  • Refresh tokens: Longer-lived (days to weeks)

4. Validate Tokens Properly

Use auth/validator on protected endpoints to verify:
  • Signature validity
  • Expiration time
  • Issuer
  • Audience
  • Required claims

5. Protect JWK Files

chmod 600 /opt/krakend/jwk-symmetric.json

Use Cases

  • Authentication Gateway: Central token issuance for microservices
  • OAuth2 Token Endpoint: Generate OAuth2-compliant access tokens
  • Session Management: Issue session tokens after login
  • API Key Rotation: Generate time-limited API keys
  • Service-to-Service Auth: Create tokens for internal service communication

Integration with /private/custom

This endpoint works seamlessly with /private/custom:
  1. Client calls POST /token to get a signed JWT
  2. Client uses the access_token to call GET /private/custom
  3. KrakenD validates the token using auth/validator
  4. If valid, the request proceeds to the backend
Full Flow:
# Step 1: Get token
TOKEN=$(curl -s -X POST http://localhost:8080/token | jq -r '.access_token')

# Step 2: Use token
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/private/custom

Troubleshooting

Token Not Signing

Issue: Backend response contains objects, not JWT strings Solution: Verify keys_to_sign matches the backend response field names.

Invalid Signature

Issue: Token validation fails on protected endpoints Solution: Ensure the same JWK file is used for both signing and validation.

Key Not Found

Issue: Error: “kid ‘sim2’ not found in JWK” Solution: Verify the kid value matches an entry in the JWK file.

Documentation Reference

For more details, see the official KrakenD documentation: https://www.krakend.io/docs/authorization/jwt-signing/

Build docs developers (and LLMs) love