diff --git a/tyk-docs/content/api-management/authentication/jwt-authorization.md b/tyk-docs/content/api-management/authentication/jwt-authorization.md new file mode 100644 index 0000000000..619db00ad7 --- /dev/null +++ b/tyk-docs/content/api-management/authentication/jwt-authorization.md @@ -0,0 +1,342 @@ +--- +title: JWT Authorization +description: How JWT authorization works in Tyk API Gateway. +tags: ["Authentication", "Authorization", "JWT", "JSON Web Tokens", "Claims", "Validation"] +keywords: ["Authentication", "Authorization", "JWT", "JSON Web Tokens", "Claims", "Validation"] +date: 2025-01-10 +--- + +## Availability + +| Component | Editions | +|-------------| ------------------------- | +| Tyk Gateway | Community and Enterprise | + +## Introduction + +[JSON Web Tokens (JWT)](https://www.jwt.io/introduction) are a popular method for client authentication and authorization that can be used to secure access to your APIs via Tyk's [JWT Auth]({{< ref "basic-config-and-security/security/authentication-authorization/json-web-tokens" >}}) method. + +After the JWT signature has been [validated]({{< ref "basic-config-and-security/security/authentication-authorization/json-web-tokens#signature-validation" >}}), Tyk uses the **claims** within the token to determine which security policies (access rights, rate limits and quotas) should be applied to the request. + +From Tyk 5.10, Tyk can perform optional [validation]({{< ref "basic-config-and-security/security/authentication-authorization/jwt-claim-validation" >}}) of these claims. + +In this page, we explain how Tyk performs JWT authorization, including how it identifies the user and the policies to be applied. + +## JWT Authorization Flow + +When a request with a JWT arrives at Tyk Gateway, after the authentication (signature and claim validation) step, Tyk performs the following steps to authorize the request: + +1. **Identity Extraction**: The user identity is extracted from the token according to this order of precedence: + - The `kid` header (unless `skipKid` is enabled) + - A custom claim (specified in `subjectClaims`) + - The standard `sub` claim (fallback) + +2. **Policy Resolution**: Tyk determines which [policy]({{< ref "api-management/policies#what-is-a-security-policy" >}}) to apply to the request: + - From scope-to-policy mapping + - From default policies + +3. **Update Session**: The [session]({{< ref "api-management/policies#what-is-a-session-object" >}}) is updated with the identity and policies. + +In the following sections, we provide a detailed explanation of each of these steps. + +## Identifying the Session Owner + +A unique identity is stored in the session object to associate it with the authenticated user. This identifier is extracted from the JWT by checking the following fields in order of precedence: + +1. The standard Key ID header (`kid`) in the JWT (unless the `skipKid` option is enabled) +2. The subject identity claim identified by the value(s) stored in `subjectClaims` (which allows API administrators to designate any JWT claim as the identity source (e.g., user_id, email, etc.). + + When multiple values are provided in the `subjectClaims` array, Tyk processes them as follows: + + 1. Tyk tries each claim **in the exact order they appear** in the array + 2. For each claim, Tyk checks if: + - The claim exists in the token + - The claim value is a string and is not empty + 3. Tyk uses the **first valid, non-empty value** it finds and stops processing further claims + 4. If none of the claims yield a valid identity, Tyk proceeds to the next stage (the `sub` claim) + + {{< note success >}} +**Note** + +Prior to Tyk 5.10, the subject identity claim was retrieved from `identityBaseField`; see [using multiple identity providers]({{< ref "#using-multiple-identity-providers" >}}) for details and for the Tyk Classic API alternative. + {{< /note >}} + +3. The `sub` [registered claim]({{< ref "api-management/authentication/jwt-claim-validation#registered-vs-custom-claims" >}}). + +**Example** + +In this example, `skipKid` has been set to `true`, so Tyk checks the `subjectClaims` and determines that the value in the custom claim `user_id` within the JWT should be used as the identity for the session object. + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + skipKid: true + subjectClaims: [user_id] +``` + +{{< note success >}} +**Note** + +Session objects can be cached to improve performance, so the identity extraction is only performed on the first request with a JWT, or when the cache is refreshed. + +{{< /note >}} + +## Identifying the Tyk Policies to be applied + +[Security Policies]({{< ref "api-management/policies" >}}) are applied (or mapped) to the session object to configure authorization for the request. Policies must be [registered]({{< ref "api-management/policies#how-you-can-create-policies" >}}) with Tyk, such that they have been allocated a unique *Tyk Policy Id*. + +Tyk supports three different types of policy mapping, which are applied in this priority order: + +1. Direct policy mapping +2. Scope policy mapping +3. Default policy mapping + +### Direct policies + +You can optionally specify policies to be applied to the session via the *policy claim* in the JWT. This is a [Private](https://datatracker.ietf.org/doc/html/rfc7519#section-4.3) Claim (not a registered claim) and can be anything you want, but typically we recommend the use of `pol`. You must instruct Tyk where to look for the policy claim by configuring the `basePolicyClaims` field in the API definition. + +Note that we typically refer to Private Claims as Custom Claims. + +In this example, Tyk has been configured to check the `pol` claim in the JWT to find the *Policy Ids* for the policies to be applied to the session object: + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + basePolicyClaims: [pol] +``` + +In the JWT, you should then provide the list of Tyk policy IDs as an array of values in that claim, for example you might declare: + +``` + "pol": ["685a8af28c24bdac0dc21c28", "685bd90b8c24bd4b6d79443d"] +``` + +{{< note success >}} +**Note** + +Prior to Tyk 5.10, the base policy claim was retrieved from `policyFieldName`; see [using multiple identity providers]({{< ref "#using-multiple-identity-providers" >}}) for details and for the Tyk Classic API alternative. +{{< /note >}} + +### Default policies + +You **must** configure one or more *default policies* that will be applied if no specific policies are identified from the JWT claims. These are configured using the `defaultPolicies` field in the API definition, which accepts a list of policy IDs. This prevents a session from being created with no authorization to interact with APIs on the Gateway. + +{{< note success >}} +**Note** + +The Gateway will return `HTTP 403 Forbidden` if no default policies are configured, if the referenced policies don’t exist, or if policies are invalid or incorrectly formatted. + +{{< /note >}} + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + defaultPolicies: + - 685a8af28c24bdac0dc21c28 + - 685bd90b8c24bd4b6d79443d +``` + +### Scope policies + +Directly mapping policies to APIs relies on the sharing of Tyk Policy IDs with the IdP (so that they can be included in the JWT) and may not provide the required flexibility. + +Tyk supports a more advanced approach where policies are applied based on scopes declared in the JWT. This keeps separation between the IdP and Tyk-specific concepts, and supports much more flexible configuration. + +Within the JWT, you identify a Private Claim that will hold the authorization (or access) scopes for the API. You then provide, within that claim, a list of *scopes*. In your API definition, you configure the `scopes.claims` to instruct Tyk where to look for the scopes and then you declare a mapping of scopes to policies within the `scopes.scopeToPolicyMapping` object. + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + scopes: + scopeToPolicyMapping: + - scope: read: users + policyId: 685bd90b8c24bd4b6d79443d + - scope: write: users + policyId: 685a8af28c24bdac0dc21c28 + claims: [accessScopes] +``` + +In this example, Tyk will check the `accessScopes` claim within the incoming JWT and apply the appropriate policy if that claim contains the value `read: users` or `write: users`. If neither scope is declared in the claim, or the claim is missing, the default policy will be applied. + +{{< note success >}} +**Note** + +Prior to Tyk 5.10, the authorization scopes claim was retrieved from `scopes.claimName`; see [using multiple identity providers]({{< ref "#using-multiple-identity-providers" >}}) for details and for the Tyk Classic API alternative. +{{< /note >}} + +#### Declaring Multiple Scopes + +You can declare multiple scopes by setting the value of the **authorization scopes claim** in one of the following ways: + +* **String with space-delimited list of values (standard format)** + + ```json + "accessScopes": "read: users write: users" + ``` + +* **Array of strings** + + ```json + "accessScopes": ["read: users", "write: users"] + ``` + +* **String with space-delimited list inside a nested key** + + ```json + "accessScopes": { "access": "read: users write: users" } + ``` + +* **Array of strings inside a nested key** + + ```json + "accessScopes": { "access": ["read: users", "write: users"] } + ``` + +**Important:** + +* If your scopes are defined inside a nested key, use **dot notation** for the `scopes.claims` value. + + * For **examples 1 and 2**, set `scopes.claims` to: + + ``` + accessScopes + ``` + * For **examples 3 and 4**, set `scopes.claims` to: + + ``` + accessScopes.access + ``` + +**Example JWT fragment:** +If this JWT is provided to an API configured as described above, Tyk will apply both policies to the session object. + +```json +{ + "sub": "1234567890", + "name": "Alice Smith", + "accessScopes": ["read: users", "write: users"] +} +``` + +### Combining policies + +Where multiple policies are mapped to a session (for example, if several scopes are declared in the JWT claim, or if you set multiple *default policies*), Tyk will apply all the matching policies to the request, combining their access rights and using the most permissive rate limits and quotas. It's important when creating those policies to ensure that they do not conflict with each other. + +Policies are combined as follows: + +1. Apply direct-mapped policies declared via `basePolicyClaims` +2. Apply scope-mapped policies declared in `scopeToPolicyMapping` based upon scopes in the JWT +3. If no policies have been applied in steps 1 or 2, apply the default policies from `defaultPolicies` + +When multiple policies are combined, the following logic is applied: + +- **access rights** A user gets access to an endpoint if ANY of the applied policies grant access +- **consumption limits** Tyk uses the most permissive values (highest quota, highest throughput ) +- **other settings** The most permissive settings from any policy are applied + +### Policy Best Practices + +When creating multiple policies that might be applied to the same JWT, we recommend using [partitioned policies]({{< ref "api-management/policies#partitioned-policies" >}}) - policies that control specific aspects of API access rather than trying to configure everything in a single policy. + +For example: + +- Create one policy that grants read-only access to specific endpoints +- Create another policy that grants write access to different endpoints +- Create a third policy that sets specific rate limits + +To ensure these policies work correctly when combined: + +- Set `per_api` to `true` in each policy. This ensures that the policy's settings only apply to the specific APIs listed in that policy, not to all APIs globally. +- Avoid listing the same `API ID` in multiple policies with conflicting settings. Instead, create distinct policies with complementary settings that can be safely combined. + + +## Session Updates + +After authenticating the token and extracting the necessary identity and policy information, Tyk creates or updates a session object that controls access to the API. + +The following [session attributes]({{< ref "api-management/policies#session-object" >}}) are modified based on the policies: + +1. **Access Rights**: Determines which API endpoints the token can access +2. **Rate Limits**: Controls how many requests per second/minute the token can make +3. **Quotas**: Sets the maximum number of requests allowed in a time period +4. **Metadata**: Custom metadata from the policies is added to the session +5. **Tags**: Policy tags are added to the session + +In addition to updating the session, Tyk extracts claims from the JWT and makes them available as context variables for use in other [middleware]({{< ref "api-management/traffic-transformation" >}}). + +{{< note success >}} + +When a JWT's claims change (for example, by configuring different scopes or policies), Tyk updates the session with the new policies on the subsequent request made with the token. + +{{< /note >}} + +## Advanced Configuration + +### Using Multiple Identity Providers + +When using multiple Identity Providers (IdPs), you may need to check different claim locations for the same information. Tyk supports definition of **multiple claim locations** for subject identity and policy IDs. + +* **Before Tyk 5.10 (and for Tyk Classic APIs):** + + * The Gateway could only check **single claims** for: + + * Subject identity + * Base policy + * Scope-to-policy mapping + * This setup didn’t support multiple IdPs using different claim names (e.g **Keycloak** uses `scope` and **Okta** uses `scp`) + +* **From Tyk 5.10 onwards (Tyk OAS APIs):** + + * You can configure **multiple claim names** for: + + * Subject identity + * Base policy + * Scope-to-policy mapping + * This allows Tyk to locate data across various tokens and IdPs more flexibly. + +**Configuration summary:** + +| API Configuration Type | Tyk Version | Subject Identity Locator | Base Policy Locator | Scope-to-Policy Mapping Locator | +| ---------------------- | ----------- | ------------------------- | ----------------------- | ------------------------------- | +| Tyk OAS | pre-5.10 | `identityBaseField` | `policyFieldName` | `scopes.claimName` | +| Tyk OAS | 5.10+ | `subjectClaims` | `basePolicyClaims` | `scopes.claims` | +| Tyk Classic | all | `jwt_identity_base_field` | `jwt_policy_field_name` | `jwt_scope_claim_name` | + +**Example configuration:** + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + # Legacy single field (still supported) + identityBaseField: "sub" + + # New multi-location support (Tyk 5.10+) + subjectClaims: + - "sub" + - "username" + - "user_id" +``` + +#### Backward Compatibility + +The new configuration is fully backward compatible: + +- Existing `identityBaseField`, `policyFieldName`, and `scopes.claimName` settings continue to work +- If both old and new fields are specified, the new fields take precedence +- When using only new fields, the first element in each array is used to set the corresponding legacy field for backward compatibility + diff --git a/tyk-docs/content/api-management/authentication/jwt-claim-validation.md b/tyk-docs/content/api-management/authentication/jwt-claim-validation.md new file mode 100644 index 0000000000..22659f850b --- /dev/null +++ b/tyk-docs/content/api-management/authentication/jwt-claim-validation.md @@ -0,0 +1,794 @@ +--- +title: JWT Claim Validation +description: How to validate JWT claims in Tyk API Gateway. +tags: ["Authentication", "JWT", "JSON Web Tokens", "Claims", "Validation"] +keywords: ["Authentication", "JWT", "JSON Web Tokens", "Claims", "Validation"] +date: 2025-01-10 +--- + +## Availability + +| Component | Editions | +|-------------| ------------------------- | +| Tyk Gateway | Community and Enterprise | + +## Introduction + +A JSON Web Token consists of three parts separated by dots: `header.payload.signature`. The payload contains the claims, a set of key-value pairs that carry information about the token and its subject. + +Tyk can validate these claims to ensure that incoming JWTs meet your security requirements before granting access to your APIs. + +By validating JWT claims, you can enforce fine-grained access control policies, ensure tokens originate from trusted sources, and verify that users have the appropriate permissions for your APIs. + +{{< note success >}} +**Viewing JWT Claims** + +To inspect the claims in a JWT, use online tools like [jwt.io](https://jwt.io) for quick debugging +{{< /note >}} + + + +## JWT Claims Fundamentals + +### Registered vs Custom Claims + +JWT claims can be categorized into two types: + +- **Registered Claims**: + + Registered Claims are standardized by the JWT specification ([RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1)) and have predefined meanings. + + These claims are further grouped into: + + - **Temporal Claims:** time-based validation + - **Identity Claims:** content-based validation + + | Claim | Name | Purpose | Type | + | ----- | --------------- | ---------------------------------------- | -------- | + | `iss` | Issuer | Identifies who issued the token | Identity | + | `aud` | Audience | Identifies who the token is intended for | Identity | + | `sub` | Subject | Identifies the subject of the token | Identity | + | `exp` | Expiration Time | When the token expires | Temporal | + | `iat` | Issued At | When the token was issued | Temporal | + | `nbf` | Not Before | When the token becomes valid | Temporal | + | `jti` | JWT ID | Unique identifier for the token | Identity | + +- **Custom Claims**: + + Custom Claims, referred to as Private Claims in the [JWT Specification](https://datatracker.ietf.org/doc/html/rfc7519#section-4.3), are application-specific and can contain any information relevant to your use case, such as user roles, permissions, department, or metadata. + +**Example JWT Payload with Both Registered and Custom Claims**: + +```json +{ + // Registered claims + "iss": "https://auth.company.com", + "aud": "api.company.com", + "sub": "user123", + "exp": 1735689600, + "iat": 1735603200, + + // Custom claims + "department": "engineering", + "role": "admin" +} +``` + +### Supported Claims and API Types + +| Claim Category | Sub-Category | Tyk OAS APIs | Tyk Classic APIs | Version | +| --------------------- | ----------------------------------------- | ------------------------- | ----------------------------- | ------------------ | +| **Registered Claims** | **Temporal** (`exp`, `iat`, `nbf`) | ✅ Yes | ✅ Yes | All versions | +| **Registered Claims** | **Identity** (`iss`, `aud`, `sub`, `jti`) | ✅ Yes | ❌ Yes | 5.10+ | +| **Custom Claims** | — | ✅ Yes | ❌ No | 5.10+ | + +### How Tyk Processes JWT Claims + +After [verifying]({{< ref "basic-config-and-security/security/authentication-authorization/json-web-tokens#signature-validation" >}}) that the token hasn't been tampered with, Tyk processes claims in this order: + +1. **Claims Extraction**: All claims from the JWT payload are extracted and stored in [context variables]({{< ref "api-management/traffic-transformation/request-context-variables" >}}) with the format `jwt_claims_CLAIMNAME`. For example, a claim named `role` becomes accessible as `jwt_claims_role`. + +2. **Claims Validation**: + - [Registered Claims Validation]({{< ref "#registered-claims-validation" >}}): Checks standard claims against your configuration + - [Custom Claims Validation]({{< ref "#custom-claims-validation" >}}): Applies your business rules to custom claims + - [Authorization]({{< ref "api-management/authentication/jwt-authorization" >}}): Uses validated claims to determine API access and apply policies + +If any validation step fails, Tyk rejects the request with a specific error message indicating which claim validation failed and why. + +## Registered Claims Validation + +[Registered Claims]({{< ref "#registered-vs-custom-claims" >}}) are grouped into: +- **Temporal claims** (time-based validation): Supported in both Tyk Classic APIs and OAS APIs +- **Identity claims** (content-based validation): Available only in Tyk OAS APIs + +### Temporal Claims + +Temporal claims define the validity period of a JWT. Tyk automatically validates these claims when present in the token. + +- **Expiration Time (exp)**: the `exp` claim specifies when the token expires (as a Unix timestamp). Tyk rejects tokens where the current time is after the expiration time. +- **Issued At (iat)**: the `iat` claim specifies when the token was issued. Tyk rejects tokens that claim to be issued in the future. +- **Not Before (nbf)**: the `nbf` claim specifies the earliest time the token can be used. Tyk rejects tokens before this time. + +#### Clock Skew Configuration + +Due to the nature of distributed systems, you may encounter clock skew between your Identity Provider and Tyk servers. You can configure tolerance for timing differences: + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + issuedAtValidationSkew: 5 # Allow tokens issued up to 5 seconds in the future + notBeforeValidationSkew: 2 # Allow tokens to be valid 2 seconds early + expiresAtValidationSkew: 2 # Allow tokens to be valid 2 seconds past expiration +``` + +- `expiresAtValidationSkew` allows recently expired tokens to be considered valid +- `issuedAtValidationSkew` allows tokens claiming future issuance to be valid +- `notBeforeValidationSkew` allows tokens to be valid before their `nbf` time + +{{< note success >}} +**Note** + +Temporal claim validation and the associated clock skew controls were supported by Tyk before 5.10.0 and also for [Tyk Classic APIs]({{< ref "api-management/gateway-config-tyk-classic#configuring-authentication-for-tyk-classic-apis" >}}) +{{< /note >}} + +### Identity Claims + +Identity claims provide information about the token's origin and intended use. Unlike temporal claims, these require explicit configuration to enable validation. + +#### Issuer Validation (iss) + +Validates that a trusted Identity Provider issued the token: + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + allowedIssuers: + - "https://auth.company.com" + - "https://auth.partner.com" +``` + +Tyk accepts tokens if the `iss` claim matches any configured issuer. If `allowedIssuers` is empty, no issuer validation is performed. + +#### Audience Validation (aud) + +Validates that the token is intended for your API: + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + allowedAudiences: + - "api.company.com" + - "mobile-app" +``` + +The `aud` claim can be a string or an array. Tyk accepts tokens if any audience value matches any configured audience. If `allowedAudiences` is empty, no audience validation is performed. + +#### Subject Validation (sub) + +Validates the token subject against allowed values: + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + allowedSubjects: + - "user" + - "service-account" + - "admin" +``` + +Useful for restricting API access to specific types of subjects or known entities. If `allowedSubjects` is empty, no subject validation is performed. + +#### JWT ID Validation (jti) + +Validates that the token contains a unique identifier: + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + jtiValidation: + enabled: true +``` + +When enabled, Tyk requires the `jti` claim to be present. This is useful for token tracking and revocation scenarios. Note that Tyk does not perform any validation on the content of the claim, only that it is present. + +### Configuration Examples + +Basic registered claims validation: + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + allowedIssuers: ["https://auth.company.com"] + allowedAudiences: ["api.company.com"] + jtiValidation: + enabled: true + expiresAtValidationSkew: 5 +``` + +Multi-IdP configuration: + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + allowedIssuers: + - "https://auth0.company.com" + - "https://keycloak.company.com" + allowedAudiences: + - "api.company.com" + - "mobile.company.com" + subjectClaims: ["sub", "username"] +``` + +In this example, we expect one Identity Provider to present the subject in the `sub` claim, and the other to present it in the `username` claim. + +## Custom Claims Validation + +Custom claims validation allows you to enforce business-specific rules on JWT tokens beyond the standard registered claims. + +**Use Cases**: + +- **Role-based access control**: Validate that users have required roles (for example, `admin`, `editor`, `viewer`) +- **Department restrictions**: Ensure users belong to authorized departments +- **Feature flags**: Check if users have access to specific features or API endpoints +- **Geographic restrictions**: Validate user location or region-based access +- **Subscription tiers**: Enforce access based on user subscription levels + +### Validation Types + +The custom claims validation supports three distinct validation types. These validation types can be applied to any custom claim in your JWT tokens, providing flexible control over your authorization logic. + +#### Required + +Required type validation ensures that a specific claim exists in the JWT token, regardless of its value. + +**Use Cases:** + +- Ensuring user metadata is present (even if empty) +- Validating that required organizational fields exist +- Confirming compliance with token structure requirements + +**Behavior:** + +- ✅ **Passes** if the claim exists with any non-null value (including empty strings, arrays, or objects) +- ❌ **Fails** if the claim is missing or explicitly set to `null` + +**Example Configuration:** + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + customClaimValidation: + department: + type: required + user_metadata: + type: required +``` + +#### Exact Match + +Exact match type validation verifies that a claim's value exactly matches one of the specified allowed values. + +**Use Cases:** + +- Role validation (e.g., `admin`, `editor`, `viewer`) +- Environment-specific access (e.g., `production`, `staging`, `development`) +- Subscription tier validation (e.g., `premium`, `standard`, `basic`) +- Boolean flag validation (`true`, `false`) + +**Behavior:** + +- ✅ Passes if the claim value exactly matches any value in the allowedValues array +- ❌ Fails if the claim value doesn't match any allowed value, if the claim is missing, or if allowedValues is empty +- Case-sensitive for string comparisons +- Type-sensitive (string "true" ≠ boolean true) + +**Example Configuration:** + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + customClaimValidation: + role: + type: exact_match + allowedValues: + - admin + - editor + - viewer + subscription_tier: + type: exact_match + allowedValues: + - premium + - standard +``` + +#### Contains + +The Contains type validation checks whether a claim's value contains or includes one of the specified values. This validation type works differently depending on the data type of the claim and is particularly useful for array-based permissions and substring matching. + +**Use Cases:** + +- Permission arrays (`["read: users", "write: posts", "admin: system"]`) +- Tag-based access control +- Partial string matching for departments or locations +- Multi-value scope validation + +**Behavior by Data Type:** + +Arrays: +- ✅ Passes if the array contains any of the specified values +- ❌ Fails if none of the specified values are found in the array + +Strings: +- ✅ Passes if the string contains any of the specified substrings +- ❌ Fails if none of the specified substrings are found + +Other Types: +- Converts to a string and performs substring matching + +Example Configuration: + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + customClaimValidation: + permissions: + type: contains + allowedValues: + - admin: system + - write:api + department_code: + type: contains + allowedValues: + - ENG + - SALES +``` + +With this configuration, a token might contain these claims: + +```json +{ + "permissions": ["read: users", "write: posts", "admin: system"], + "department_code": "ENG-BACKEND", +} +``` + +In this example: +- `permissions` validation passes because the array contains `"admin: system"` +- `department_code` validation passes because the string contains `"ENG"` + +### Data Type Support + +The framework is designed to handle the diverse data types commonly found in JWT tokens. The validation behavior adapts intelligently based on the actual data type of each claim, ensuring robust and predictable validation across different token structures. + +#### Supported Data Types + +##### String Values + +String claims are the most common type in JWT tokens and support all three validation types with intuitive behavior. + +**Validation behavior** + +- **Required**: Passes if the string exists (including empty strings `""`) +- **Exact Match**: Performs case-sensitive string comparison +- **Contains**: Checks if the string contains any of the specified substrings + +**Example** + +Claims: + +```json +{ + "department": "Engineering", + "user_id": "user123", + "email": "john.doe@company.com" +} +``` + +Validation configuration: + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + customClaimValidation: + department: + type: exact_match + allowedValues: + - Engineering + - Sales + - Marketing + email: + type: contains + allowedValues: + - "@company.com" + - "@partner.com" +``` + +##### Numeric Values + +Numeric claims (integers and floating-point numbers) are validated with type-aware comparison logic. + +**Validation behavior** + +- **Required**: Passes if the number exists (including `0`) +- **Exact Match**: Performs numeric equality comparison (`42` matches `42.0`) +- **Contains**: Converts to a string and performs substring matching + +**Example** + +Claims: + +```json +{ + "user_level": 5, + "account_balance": 1250.75, + "login_count": 0 +} +``` + +Validation configuration: + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + customClaimValidation: + user_level: + type: exact_match + allowedValues: + - 1 + - 2 + - 3 + - 4 + - 5 + account_balance: + type: required +``` + +##### Boolean Values + +Boolean claims are commonly used for feature flags and permission toggles. + +**Validation Behavior** + +- **Required**: Passes if the boolean exists (`true` or `false`) +- **Exact Match**: Performs strict boolean comparison +- **Contains**: Converts to string (`"true"` or `"false"`) and performs substring matching + +**Example** + +Claims: + +```json +{ + "is_admin": true, + "email_verified": false, + "beta_features": true +} +``` + +Validation configuration: + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + customClaimValidation: + is_admin: + type: exact_match + allowedValues: + - true + email_verified: + type: required +``` + +##### Array Values + +Arrays are particularly powerful for permission systems and multi-value attributes. + +**Validation behavior** + +- **Required**: Passes if the array exists (including empty arrays `[]`) +- **Exact Match**: Checks if the entire array exactly matches one of the allowed arrays +- **Contains**: Checks if the array contains any of the specified values (most common use case) + +**Example** + +Claims: + +```json +{ + "roles": ["user", "editor"], + "permissions": ["read: posts", "write: posts", "delete: own"], + "departments": ["engineering", "product"], + "tags": [] +} +``` + +Validation configuration: + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + customClaimValidation: + permissions: + type: contains + allowedValues: + - write: posts + - admin: system + roles: + type: contains + allowedValues: + - admin + - editor + - moderator + tags: + type: required +``` + +##### Object Values + +Complex object claims can be validated, though typically you'll want to validate specific nested properties using [dot notation]({{< ref "#nested-claims" >}}). + +**Validation Behavior** + +- **Required**: Passes if the object exists (including empty objects `{}`) +- **Exact Match**: Performs deep object comparison (rarely used) +- **Contains**: Converts to a JSON string and performs substring matching + +**Example** + +Claims: + +```json +{ + "user_metadata": { + "department": "Engineering", + "level": 5, + "location": "US" + }, + "preferences": {} +} +``` + +Configuration: + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + customClaimValidation: + user_metadata: + type: required + preferences: + type: required +``` + +##### Type Coercion and Edge Cases + +**Null and Undefined Values** + +- null values: Always fail validation (treated as missing) +- undefined/missing claims: Fail all validation types except when validation is not configured + +**Mixed-Type Arrays** + +Arrays containing different data types are supported. The `contains` validation will attempt to match values using appropriate type comparison, + +```json +{ + "mixed_permissions": ["read", 42, true, "admin"] +} +``` + +**Type Mismatches** + +When the expected value type doesn't match the claim type, Tyk performs intelligent conversion: + +- Numbers to strings: `42` becomes `"42"` +- Booleans to strings: `true` becomes "`true"` +- Objects/arrays to strings: Converted to JSON representation + +##### Best Practices + +- Be Explicit About Types: When configuring `allowedValues`, use the same data type as expected in the token +- Use Arrays for Multi-Value Validation: Prefer array-based claims for permissions and roles +- Consider Empty Values: Remember that empty strings, arrays, and objects pass `required` validation +- Test Type Coercion: Verify behavior when token types don't match expected types + +### Nested Claims + +JSON Web Tokens often contain complex, hierarchical data structures with nested objects and arrays. Tyk's custom claims validation framework supports validating nested claim structures using dot notation syntax. + +**Basic Syntax:** + +- `user.name` - Access the `name` property within the `user` object +- `permissions.0.resource` - Access the `resource` property of the first element in the `permissions` array + +{{< note success >}} +**Dot Notation** + +Tyk uses [gjson](https://github.com/tidwall/gjson) to parse dot notation paths. + +{{< /note >}} + +#### Nested Object Validation + +The most common use case for dot notation is validating properties within nested objects, such as user metadata, organizational information, or configuration settings. + +**Example Token** + +```json +{ + "user": { + "name": "John Doe", + "email": "john.doe@company.com", + "profile": { + "department": "Engineering", + "level": "senior", + "location": { + "country": "US", + "region": "West" + } + } + } +} +``` + +You could set the following configuration to validate the requester's department and level: + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + customClaimValidation: + user.profile.department: + type: exact_match + allowedValues: + - Engineering + - Sales + - Marketing + user.profile.level: + type: contains + allowedValues: + - senior + - lead + - principal +``` + + +#### Nested Array Validation + +Arrays are commonly used in JWT claims to represent lists of permissions, roles, or other multi-value attributes. Tyk supports validating specific elements within arrays using dot notation with numeric indices. + +**Example Token** + +```json +{ + "permissions": [ + { + "resource": "users", + "actions": ["read", "write"] + }, + { + "resource": "reports", + "actions": ["read"] + } + ] +} +``` + +You can validate specific array elements: + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + customClaimValidation: + "permissions.0.resource": + type: exact_match + allowedValues: ["users"] + "permissions.1.actions.0": + type: exact_match + allowedValues: ["read"] +``` + +{{< note warning >}} +**Dot Notation** + +When a nested path doesn't exist (e.g., `user.profile.level` but `profile` doesn't exist) or when an array index is out of bounds (e.g., `permissions.999.resource`), the claim is treated as missing. This will cause validation to fail for blocking rules or generate a warning for non-blocking rules. + +{{< /note >}} + +#### Recommendations + +Test your nested claim validation rules with representative JWT tokens to ensure they behave as expected. Use online tools like [gjson.dev](https://gjson.dev/) to experiment with dot notation paths and verify they correctly access the desired values. + +### Non-blocking Validation + +Non-blocking validation allows JWT claims to fail validation with a warning logged, while still permitting the request to proceed. + +This behavior allows you to: + +- Monitor how new validation rules would affect traffic without disrupting users +- Gradually roll out stricter validation requirements +- Debug validation issues in production environments + +#### How Non-blocking Validation Works + +When configured, a validation rule can be set to "non-blocking" mode, which means: + +1. If validation passes, the request proceeds normally +2. If validation fails, instead of rejecting the request: + - A warning is logged to the Tyk Gateway log file at the `WARN` log level + - The validation process continues to evaluate other custom claims + - The request is allowed to proceed to the upstream API + +#### Configuring Non-Blocking Mode + +Non-blocking mode can be configured for any custom claim validation rule with the addition of the boolean `nonBlocking` flag, for example: + +```yaml +x-tyk-api-gateway: + server: + authentication: + securitySchemes: + jwtAuth: + customClaimValidation: + user.profile.department: + type: exact_match + allowedValues: + - Engineering + - Sales + - Marketing + user.preferences.notifications: + type: required + nonBlocking: true +``` + +The `nonBlocking` flag in the validation rule for `user.preferences.notifications` means that if this claim is missing from the received token, the token will not fail validation, but a warning will be logged. \ No newline at end of file diff --git a/tyk-docs/content/api-management/client-authentication.md b/tyk-docs/content/api-management/client-authentication.md index b12580ce92..7a44eef999 100644 --- a/tyk-docs/content/api-management/client-authentication.md +++ b/tyk-docs/content/api-management/client-authentication.md @@ -196,7 +196,7 @@ There could be cases when you don’t need to introspect a JWT access token from - a base64 encoded static secret - a valid JWK url in plain text - a valid JWK url in base64 encoded format -- `issuedAtValidationSkew` , `notBeforeValidationSkew`, `expiresAtValidationSkew` can be used to [configure clock skew]({{< ref "basic-config-and-security/security/authentication-authorization/json-web-tokens#jwt-validity-and-clock-skew" >}}) for json web token validation. +- `issuedAtValidationSkew` , `notBeforeValidationSkew`, `expiresAtValidationSkew` can be used to [configure clock skew]({{< ref "api-management/authentication/jwt-claim-validation#clock-skew-configuration" >}}) for json web token validation. - `identityBaseField` - the identity key name for claims. If empty it will default to `sub`. ##### Example: Tyk OAS API definition with JWT validation enabled diff --git a/tyk-docs/content/basic-config-and-security/security/authentication-authorization/json-web-tokens.md b/tyk-docs/content/basic-config-and-security/security/authentication-authorization/json-web-tokens.md index fca875342b..a50efd0f54 100644 --- a/tyk-docs/content/basic-config-and-security/security/authentication-authorization/json-web-tokens.md +++ b/tyk-docs/content/basic-config-and-security/security/authentication-authorization/json-web-tokens.md @@ -183,7 +183,7 @@ This diagram outlines the flow when using JWT Auth to secure access to your API. - if signature validation fails, the request is rejected - if the token is valid and not expired, the request is authenticated as coming from the client, and is accepted -5. Next, Tyk will create an internal session for the request which will be used to control access rights, rate limits, usage quotas and in tracking logs (step 5). The session is linked to Alice using an identity that is [extracted from the JWT claims]({{< ref "basic-config-and-security/security/authentication-authorization/json-web-tokens#identifying-the-session-owner" >}}). +5. Next, Tyk will create an internal session for the request which will be used to control access rights, consumption limits and to identify the request in tracking logs (step 5). The session is linked to Alice using an identity that is [extracted from the JWT claims]({{< ref "api-management/authentication/jwt-authorization#identifying-the-session-owner" >}}). 6. In step 6 Tyk will proceed to enforce authorization by checking other claims to determine which Security Policies should be applied to the session: - check for the value in the policy claim within the JWT (identified by the value stored in `basePolicyClaims`) @@ -326,899 +326,6 @@ x-tyk-api-gateway: If both `.source` and `.jwksURIs` are configured, the latter will take precedence. {{< /note >}} -## Claim Validation - -JSON Web Tokens contain claims - key-value pairs that provide information about the token and its subject. Tyk can validate these claims to ensure that incoming JWTs meet your security requirements before granting access to your APIs. - -Tyk supports validation of both: - -- **Registered claims**: standardized claims defined in the JWT specification (such as `iss`, `aud`, `exp`) -- **Custom claims**: application-specific claims that contain business logic or additional metadata -By validating JWT claims, you can enforce fine-grained access control policies, ensure tokens originate from trusted sources, and verify that users have the appropriate permissions for your APIs. - -{{< note success >}} -**Note** - -JWT claim validation - with the exception of [temporal claims]({{< ref "basic-config-and-security/security/authentication-authorization/json-web-tokens#temporal-claims-exp-iat-nbf" >}}) - is available exclusively for Tyk OAS APIs and from Tyk 5.10.0 onwards. -{{< /note >}} - -### JWT Claims Fundamentals - -#### What are JWT Claims? -A JSON Web Token consists of three parts separated by dots: `header.payload.signature`. The payload contains the claims - a set of key-value pairs that carry information about the token and its subject. - -```json -{ - "iss": "https://auth.company.com", - "aud": "api.company.com", - "sub": "user123", - "exp": 1735689600, - "iat": 1735603200, - "department": "engineering", - "role": "admin" -} -``` - -Claims serve different purposes: - -- **Identity information**: who the token represents (`sub`, `iss`) -- **Access control**: what the token can access (`aud`, custom permissions) -- **Validity period**: when the token is valid (`exp`, `iat`, `nbf`) -- **Business logic**: application-specific data (`department`, `role`) - -#### Registered vs Custom Claims - -Registered Claims are standardized by the JWT specification ([RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1)) and have predefined meanings: - -| Claim | Name | Purpose | -|-------|------------|---------| -| `iss` | Issuer | Identifies who issued the token | -| `aud` | Audience | Identifies who the token is intended for | -| `sub` | Subject | Identifies the subject of the token | -| `exp` | Expiration Time | When the token expires | -| `iat` | Issued At | When the token was issued | -| `nbf` | Not Before | When the token becomes valid | -| `jti` | JWT ID | Unique identifier for the token | - -Custom Claims are application-specific and can contain any information relevant to your use case, such as user roles, permissions, department, or metadata. - -#### How Tyk Processes JWT Claims - -After [verifying]({{< ref "basic-config-and-security/security/authentication-authorization/json-web-tokens#signature-validation" >}}) that the token hasn't been tampered with, Tyk processes claims in this order: - -- [Registered Claims Validation]({{< ref "basic-config-and-security/security/authentication-authorization/json-web-tokens#registered-claims-validation" >}}): Checks standard claims against your configuration -- [Custom Claims Validation]({{< ref "basic-config-and-security/security/authentication-authorization/json-web-tokens#custom-claims-validation" >}}): Applies your business rules to custom claims -- [Authorization]({{< ref "basic-config-and-security/security/authentication-authorization/json-web-tokens#managing-authorization" >}}): Uses validated claims to determine API access and apply policies - -If any validation step fails, Tyk rejects the request with a specific error message indicating which claim validation failed and why. - -### Registered Claims Validation - -Tyk can validate the seven registered JWT claims defined in [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1). These claims are grouped into **temporal claims** (time-based validation) and **identity claims** (content-based validation). - -#### Temporal Claims (exp, iat, nbf) - -Temporal claims define when a JWT is valid. Tyk automatically validates these claims when present in the token. - -- **Expiration Time (exp)**: the `exp` claim specifies when the token expires (as a Unix timestamp). Tyk rejects tokens where the current time is after the expiration time. -- **Issued At (iat)**: the `iat` claim specifies when the token was issued. Tyk rejects tokens that claim to be issued in the future. -- **Not Before (nbf)**: the `nbf` claim specifies the earliest time the token can be used. Tyk rejects tokens before this time. - -##### Clock Skew Configuration - -Due to the nature of distributed systems, you may encounter clock skew between your Identity Provider and Tyk servers. You can configure tolerance for timing differences: - -```yaml -x-tyk-api-gateway: - server: - authentication: - securitySchemes: - jwtAuth: - issuedAtValidationSkew: 5 # Allow tokens issued up to 5 seconds in the future - notBeforeValidationSkew: 2 # Allow tokens to be valid 2 seconds early - expiresAtValidationSkew: 2 # Allow tokens to be valid 2 seconds past expiration -``` - -- `expiresAtValidationSkew` allows recently expired tokens to be considered valid -- `issuedAtValidationSkew` allows tokens claiming future issuance to be valid -- `notBeforeValidationSkew` allows tokens to be valid before their `nbf` time - -{{< note success >}} -**Note** - -Temporal claim validation and the associated clock skew controls were supported by Tyk prior to 5.10.0 and also for [Tyk Classic APIs]({{< ref "api-management/gateway-config-tyk-classic#configuring-authentication-for-tyk-classic-apis" >}}) -{{< /note >}} - -#### Identity Claims (iss, aud, sub, jti) - -Identity claims provide information about the token's origin and intended use. Unlike temporal claims, these require explicit configuration to enable validation. - -##### Issuer Validation (iss) - -Validates that the token was issued by a trusted Identity Provider: - -```yaml -x-tyk-api-gateway: - server: - authentication: - securitySchemes: - jwtAuth: - allowedIssuers: - - "https://auth.company.com" - - "https://auth.partner.com" -``` - -Tyk accepts tokens if the `iss` claim matches any configured issuer. If `allowedIssuers` is empty, no issuer validation is performed. - -##### Audience Validation (aud) - -Validates that the token is intended for your API: - -```yaml -x-tyk-api-gateway: - server: - authentication: - securitySchemes: - jwtAuth: - allowedAudiences: - - "api.company.com" - - "mobile-app" -``` - -The `aud` claim can be a string or array. Tyk accepts tokens if any audience value matches any configured audience. If `allowedAudiences` is empty, no audience validation is performed. - -##### Subject Validation (sub) - -Validates the token subject against allowed values: - -```yaml -x-tyk-api-gateway: - server: - authentication: - securitySchemes: - jwtAuth: - allowedSubjects: - - "user" - - "service-account" - - "admin" -``` - -Useful for restricting API access to specific types of subjects or known entities. If `allowedSubjects` is empty, no audience validation is performed. - -##### JWT ID Validation (jti) - -Validates that the token contains a unique identifier: - -```yaml -x-tyk-api-gateway: - server: - authentication: - securitySchemes: - jwtAuth: - jtiValidation: - enabled: true -``` - -When enabled, Tyk requires the `jti` claim to be present. This is useful for token tracking and revocation scenarios. Note that Tyk does not perform any validation on the content of the claim, only that it is present. - -#### Configuration Examples - -Basic registered claims validation: - -```yaml -x-tyk-api-gateway: - server: - authentication: - securitySchemes: - jwtAuth: - allowedIssuers: ["https://auth.company.com"] - allowedAudiences: ["api.company.com"] - jtiValidation: - enabled: true - expiresAtValidationSkew: 5 -``` - -Multi-IdP configuration: - -```yaml -x-tyk-api-gateway: - server: - authentication: - securitySchemes: - jwtAuth: - allowedIssuers: - - "https://auth0.company.com" - - "https://keycloak.company.com" - allowedAudiences: - - "api.company.com" - - "mobile.company.com" - subjectClaims: ["sub", "username"] -``` - -In this example we expect one Identity Provider to present the subject in the `sub` claim, and the other to present it in the `username` claim. - -### Custom Claims Validation - -Custom claims validation allows you to enforce business-specific rules on JWT tokens beyond the standard registered claims. You can validate application-specific data such as user roles, departments, permissions, or any other business logic embedded in your JWTs. - -Custom claims are commonly used for such purposes as: - -- **Role-based access control**: Validate that users have required roles (for example `admin`, `editor` ,`viewer`) -- **Department restrictions**: Ensure users belong to authorized departments -- **Feature flags**: Check if users have access to specific features or API endpoints -- **Geographic restrictions**: Validate user location or region-based access -- **Subscription tiers**: Enforce access based on user subscription levels - -Key Benefits of Tyk's comprehensive custom claim validation framework include: - -- **Enhanced Security**: Add additional layers of validation beyond standard JWT verification -- **Business Logic Enforcement**: Implement complex authorization rules at the gateway level -- **Flexible Validation**: Support for multiple validation types and data structures -- **Non-blocking Options**: Configure warnings instead of blocking requests for certain validations -- **Nested Data Support**: Validate complex, nested JSON structures within claims - -#### Validation Types - -Three distinct validation types are supported by the custom claims validation framework, each designed for different use cases and levels of validation strictness. These validation types can be applied to any custom claim in your JWT tokens, providing flexible control over your authorization logic. - -##### Required - -Required type validation ensures that a specific claim exists in the JWT token, regardless of its value. This is the most basic form of validation and is useful when you need to guarantee that certain information is present in tokens. - -**Use Cases:** - -- Ensuring user metadata is present (even if empty) -- Validating that required organizational fields exist -- Confirming compliance with token structure requirements - -**Behavior:** - -- ✅ **Passes** if the claim exists with any non-null value (including empty strings, arrays, or objects) -- ❌ **Fails** if the claim is missing or explicitly set to `null` - -**Example Configuration:** - -```yaml -customClaimValidation: - department: - type: required - user_metadata: - type: required -``` - -##### Exact Match - -Exact match type validation verifies that a claim's value exactly matches one of the specified allowed values. This provides precise control over acceptable claim values and is ideal for role-based access control and categorical validations. - -**Use Cases:** - -- Role validation (e.g. `admin`, `editor`, `viewer`) -- Environment-specific access (e.g. `production`, `staging`, `development`) -- Subscription tier validation (e.g. `premium`, `standard`, `basic`) -- Boolean flag validation (`true`, `false`) - -**Behavior:** - -- ✅ Passes if the claim value exactly matches any value in the allowedValues array -- ❌ Fails if the claim value doesn't match any allowed value or if the claim is missing -- Case-sensitive for string comparisons -- Type-sensitive (string "true" ≠ boolean true) - -**Example Configuration:** - -```yaml -customClaimValidation: - role: - type: exact_match - allowedValues: - - admin - - editor - - viewer - subscription_tier: - type: exact_match - allowedValues: - - premium - - standard -``` - -##### Contains - -The Contains type validation checks whether a claim's value contains or includes one of the specified values. This validation type works differently depending on the data type of the claim and is particularly useful for array-based permissions and substring matching. - -**Use Cases:** - -- Permission arrays (`["read:users", "write:posts", "admin:system"]`) -- Tag-based access control -- Partial string matching for departments or locations -- Multi-value scope validation - -**Behavior by Data Type:** - -Arrays: -- ✅ Passes if the array contains any of the specified values -- ❌ Fails if none of the specified values are found in the array - -Strings: -- ✅ Passes if the string contains any of the specified substrings -- ❌ Fails if none of the specified substrings are found - -Other Types: -- Converts to string and performs substring matching - -Example Configuration: - -```yaml -customClaimValidation: - permissions: - type: contains - allowedValues: - - admin:system - - write:api - department_code: - type: contains - allowedValues: - - ENG - - SALES -``` - -With this configuration, a token might contain these claims: - -```json -{ - "permissions": ["read:users", "write:posts", "admin:system"], - "department_code": "ENG-BACKEND", -} -``` - -In this example: -- `permissions` validation passes because the array contains `"admin:system"` -- `department_code` validation passes because the string contains `"ENG"` - -#### Data Type Support - -The framework is designed to handle the diverse data types commonly found in JWT tokens. The validation behavior adapts intelligently based on the actual data type of each claim, ensuring robust and predictable validation across different token structures. - -##### Supported Data Types - -###### String Values - -String claims are the most common type in JWT tokens and support all three validation types with intuitive behavior. - -**Validation behavior** - -- **Required**: Passes if the string exists (including empty strings `""`) -- **Exact Match**: Performs case-sensitive string comparison -- **Contains**: Checks if the string contains any of the specified substrings - -**Example** - -Claims: - -```json -{ - "department": "Engineering", - "user_id": "user123", - "email": "john.doe@company.com" -} -``` - -Validation configuration: - -```yaml -customClaimValidation: - department: - type: exact_match - allowedValues: - - Engineering - - Sales - - Marketing - email: - type: contains - allowedValues: - - "@company.com" - - "@partner.com" -``` - -###### Numeric Values - -Numeric claims (integers and floating-point numbers) are validated with type-aware comparison logic. - -**Validation behavior** - -- **Required**: Passes if the number exists (including `0`) -- **Exact Match**: Performs numeric equality comparison (`42` matches `42.0`) -- **Contains**: Converts to string and performs substring matching - -**Example** - -Claims: - -```json -{ - "user_level": 5, - "account_balance": 1250.75, - "login_count": 0 -} -``` - -Validation configuration: - -```yaml -customClaimValidation: - user_level: - type: exact_match - allowedValues: - - 1 - - 2 - - 3 - - 4 - - 5 - account_balance: - type: required -``` - -###### Boolean Values - -Boolean claims are commonly used for feature flags and permission toggles. - -**Validation Behavior** - -- **Required**: Passes if the boolean exists (`true` or `false`) -- **Exact Match**: Performs strict boolean comparison -- **Contains**: Converts to string (`"true"` or `"false"`) and performs substring matching - -**Example** - -Claims: - -```json -{ - "is_admin": true, - "email_verified": false, - "beta_features": true -} -``` - -Validation configuration: - -```yaml -customClaimValidation: - is_admin: - type: exact_match - allowedValues: - - true - email_verified: - type: required -``` - -###### Array Values - -Arrays are particularly powerful for permission systems and multi-value attributes. - -**Validation behavior** - -- **Required**: Passes if the array exists (including empty arrays `[]`) -- **Exact Match**: Checks if the entire array exactly matches one of the allowed arrays -- **Contains**: Checks if the array contains any of the specified values (most common use case) - -**Example** - -Claims: - -```json -{ - "roles": ["user", "editor"], - "permissions": ["read:posts", "write:posts", "delete:own"], - "departments": ["engineering", "product"], - "tags": [] -} -``` - -Validation configuration: - -```yaml -customClaimValidation: - permissions: - type: contains - allowedValues: - - write:posts - - admin:system - roles: - type: contains - allowedValues: - - admin - - editor - - moderator - tags: - type: required -``` - -###### Object Values - -Complex object claims can be validated, though typically you'll want to validate specific nested properties using [dot notation]({{< ref "basic-config-and-security/security/authentication-authorization/json-web-tokens#nested-claims-dot-notation" >}}). - -**Validation Behavior** - -- **Required**: Passes if the object exists (including empty objects `{}`) -- **Exact Match**: Performs deep object comparison (rarely used) -- **Contains**: Converts to JSON string and performs substring matching - -**Example** - -Claims: - -```json -{ - "user_metadata": { - "department": "Engineering", - "level": 5, - "location": "US" - }, - "preferences": {} -} -``` - -Configuration: - -```yaml -customClaimValidation: - user_metadata: - type: required - preferences: - type: required -``` - -###### Type Coercion and Edge Cases - -**Null and Undefined Values** - -- null values: Always fail validation (treated as missing) -- undefined/missing claims: Fail all validation types except when validation is not configured - -**Mixed-Type Arrays** - -Arrays containing different data types are supported. The `contains` validation will attempt to match values using appropriate type comparison, - -```json -{ - "mixed_permissions": ["read", 42, true, "admin"] -} -``` - -**Type Mismatches** - -When the expected value type doesn't match the claim type, Tyk performs intelligent conversion: - -- Numbers to strings: `42` become `"42"` -- Booleans to strings: `true` becomes "`true"` -- Objects/arrays to strings: Converted to JSON representation - -###### Best Practices - -- Be Explicit About Types: When configuring `allowedValues`, use the same data type as expected in the token -- Use Arrays for Multi-Value Validation: Prefer array-based claims for permissions and roles -- Consider Empty Values: Remember that empty strings, arrays, and objects pass `required` validation -- Test Type Coercion: Verify behavior when token types don't match expected types - -#### Nested Claims (Dot Notation) - -JSON Web Tokens often contain complex, hierarchical data structures with nested objects and arrays. Tyk's custom claims validation framework supports dot notation syntax, allowing you to validate specific values deep within nested claim structures without having to validate entire objects. - -Dot notation uses periods (`.`) to traverse nested object properties, similar to JavaScript object property access. This enables precise validation of deeply nested values while maintaining clean, readable configuration. - -**Basic Syntax:** - -- `user.name` - Access the `name` property within the `user` object -- `metadata.department.code` - Access the `code` property within `department` within `metadata` -- `permissions.api.read` - Access the `read` property within `api` within `permissions` - -##### Nested Object Validation - -The most common use case for dot notation is validating properties within nested objects, such as user metadata, organizational information, or configuration settings. - -[OAuth 2.0 Token Exchange](https://datatracker.ietf.org/doc/html/rfc8693#name-act-actor-claim) relies upon nesting for the `act` (actor) claim. - -**Example Token** - -```json -{ - "user": { - "name": "John Doe", - "email": "john.doe@company.com", - "profile": { - "department": "Engineering", - "level": "senior", - "location": { - "country": "US", - "region": "West" - } - } - } -} -``` - -You could set the following configuration to validate the requester's department and level: - -```yaml -{ - "customClaimValidation": { - "user.profile.department": { - "type": "exact_match", - "allowedValues": ["Engineering", "Sales", "Marketing"] - }, - "user.profile.level": { - "type": "contains", - "allowedValues": ["senior", "lead", "principal"] - } - } -} -``` - -#### Non-blocking Validation - -The non-blocking validation feature specifically enables a gradual rollout approach to validation rules by allowing you to monitor validation failures without rejecting requests. - -##### How Non-blocking Validation Works - -When configured, a validation rule can be set to "non-blocking" mode, which means: - -1. If validation passes, the request proceeds normally -2. If validation fails, instead of rejecting the request: - - a warning is logged in the gateway [system logs]({{< ref "api-management/logs-metrics#system-logs" >}}) - - the request is allowed to proceed to the upstream API - -This allows you to: - -- Monitor how new validation rules would affect traffic without disrupting users -- Gradually roll out stricter validation requirements -- Debug validation issues in production environments - -##### Configuring Non-Blocking Mode - -Non-blocking mode can be configured for any custom claim validation rule with the addition of the boolean `nonBlocking` flag, for example: - -```yaml -{ - "customClaimValidation": { - "user.profile.department": { - "type": "exact_match", - "allowedValues": ["Engineering", "Sales", "Marketing"] - }, - "user.profile.level": { - "type": "contains", - "allowedValues": ["senior", "lead", "principal"] - }, - "user.preferences.notifications": { - "type": "required", - "nonBlocking": true - } - } -} -``` - -The `nonBlocking` flag in the validation rule for `user.preferences.notifications` means that if this claim is missing from the received token, the token will not fail validation, but a warning will be logged. - -## Managing Authorization - -The claims within the JSON Web Token are used to configure the Authorization for the request - i.e. which resources it can access and what limits should be applied to that access. - -Tyk creates an internal [session object]({{< ref "api-management/policies#what-is-a-session-object" >}}) for the request. The session is used to apply the appropriate security policies to ensure that rate limits, quotas, and access controls specific to the user are correctly applied. The session also enable tracking and analytics for the user's API usage. - -### Identifying the Session Owner - -In order that this session can be correctly associated with the authenticated user, Tyk must extract a unique identity from the token. - -The JWT specification [defines](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2) the optional `sub` claim which identifies the principal that is the subject of the JWT. In OAuth/OIDC contexts this will usually be the end user (resource owner) on whose behalf the token was issued and so is typically used to identify the session owner. - -Tyk provides a flexible approach to identifying the session owner, to account for other use cases where the `sub` field is not supplied or appropriate. The identity is extracted from the token by checking the following fields in order of precedence: - -1. The standard Key ID header (`kid`) in the JWT (unless the `skipKid` option is enabled) -2. The subject identity claim identified by the value(s) stored in `subjectClaims` (which allows API administrators to designate any JWT claim as the identity source (e.g., user_id, email, etc.). -3. The `sub` registered claim - -{{< note success >}} -**Note** - -Prior to Tyk 5.10, the subject identity claim was retrieved from `identityBaseField`; see [using multiple identity providers]({{< ref "basic-config-and-security/security/authentication-authorization/json-web-tokens#using-multiple-identity-providers" >}}) for details and for the Tyk Classic API alternative. -{{< /note >}} - -When an identity has been determined, it is stored in the session object in three locations: -- in the `Alias` field -- it is used to generate a hashed session Id stored in the `keyID` field -- in the session metadata as `TykJWTSessionID` - -Note that session objects can be cached to improve performance, so the identity extraction is only performed on the first request with a JWT, or when the cache is refreshed. - -In this example, `skipKid` has been set to `true`, so Tyk checks the `subjectClaims` and determines that the value in the custom claim `user_id` within the JWT should be used as the identity for the session object. - -```yaml -x-tyk-api-gateway: - server: - authentication: - securitySchemes: - jwtAuth: - skipKid: true - subjectClaims: [user_id] -``` - -### Identifying the Tyk Policies to be applied - -[Security Policies]({{< ref "api-management/policies" >}}) are applied (or mapped) to the session object to configure authorization for the request. Policies must be [registered]({{< ref "api-management/policies#how-you-can-create-policies" >}}) with Tyk, such that they have been allocated a unique *Tyk Policy Id*. - -Tyk supports three different types of policy mapping, which are applied in this priority order: - -1. Direct policy mapping -2. Scope policy mapping -3. Default policy mapping - -Note that, whilst a *default policy* must be configured for each API using JWT Auth, this will only be applied if there are no policies mapped in step 1 or 2. If *scope policies* are activated, these will be applied on top of the previously applied direct policies as explained in more detail in the section on [combining policies]({{< ref "basic-config-and-security/security/authentication-authorization/json-web-tokens#combining-policies" >}}). - -#### Direct policies - -You can optionally specify policies to be applied to the session via the *policy claim* in the JWT. This is a [Private Claim](https://datatracker.ietf.org/doc/html/rfc7519#section-4.3) and can be anything you want, but typically we recommend the use of `pol`. You must instruct Tyk where to look for the policy claim by configuring the `basePolicyClaims` field in the API definition. - -In this example, Tyk has been configured to check the `pol` claim in the JWT to find the *Policy Ids* for the policies to be applied to the session object: - -```yaml -x-tyk-api-gateway: - server: - authentication: - securitySchemes: - jwtAuth: - basePolicyClaims: [pol] -``` - -In the JWT, you should then provide the list of policy Ids as an array of values in that claim, for example you might declare: - -``` - "pol": ["685a8af28c24bdac0dc21c28", "685bd90b8c24bd4b6d79443d"] -``` - -{{< note success >}} -**Note** - -Prior to Tyk 5.10, the base policy claim was retrieved from `policyFieldName`; see [using multiple identity providers]({{< ref "basic-config-and-security/security/authentication-authorization/json-web-tokens#using-multiple-identity-providers" >}}) for details and for the Tyk Classic API alternative. -{{< /note >}} - -#### Default policies - -You **must** configure one or more *default policies* that will be applied if no specific policies are identified in the JWT claims. These are configured using the `defaultPolicies` field in the API definition, which accepts a list of policy Ids. - -```yaml -x-tyk-api-gateway: - server: - authentication: - securitySchemes: - jwtAuth: - defaultPolicies: - - 685a8af28c24bdac0dc21c28 - - 685bd90b8c24bd4b6d79443d -``` - -#### Scope policies - -Directly mapping policies to APIs relies upon the sharing of Tyk Policy Ids with the IdP (so that they can be included in the JWT) and may not provide the flexibility required. Tyk supports a more advanced approach where policies are applied based upon *scopes* declared in the JWT. This keeps separation between the IdP and Tyk-specific concepts, and supports much more flexible configuration. - -Within the JWT, you identify a Private Claim that will hold the authorization (or access) scopes for the API. You then provide, within that claim, a list of *scopes*. In your API definition, you configure the `scopes.claims` to instruct Tyk where to look for the scopes and then you declare a mapping of scopes to policies within the `scopes.scopeToPolicyMapping` object. - -```yaml -x-tyk-api-gateway: - server: - authentication: - securitySchemes: - jwtAuth: - scopes: - scopeToPolicyMapping: - - scope: read:users - policyId: 685bd90b8c24bd4b6d79443d - - scope: write:users - policyId: 685a8af28c24bdac0dc21c28 - claims: [accessScopes] -``` - -In this example, Tyk will check the `accessScopes` claim within the incoming JWT and apply the appropriate policy if that claim contains the value `read:users` or `write:users`. If neither scope is declared in the claim, or the claim is missing, then the default policy will be applied. - -{{< note success >}} -**Note** - -Prior to Tyk 5.10, the authorization scopes claim was retrieved from `scopes.claimName`; see [using multiple identity providers]({{< ref "basic-config-and-security/security/authentication-authorization/json-web-tokens#using-multiple-identity-providers" >}}) for details and for the Tyk Classic API alternative. -{{< /note >}} - -Multiple scopes can be declared by setting the value of the authorization scopes claim in any of four configurations: - -- a string with space delimited list of values (by standard)
- `"permissions": "read:users write:users"` -- an array of strings
- `"permissions": ["read:users", "write:users"]` -- a string with space delimited list of values inside a nested key
- `"permissions": { "access": "read:users write:users" }` -- an array of strings inside a nested key
- `"permissions": { "access": ["read:users", "write:users"] }` - -If there is a nested key then you must use dot notation in the value configured for `scopes.claims` so, for the first two examples above, `scopes.claims` should be set to `permissions` whilst for the the two nested examples you would use `permissions.access`. - -This example of a fragment of a JWT, if provided to an API with the configuration above, will cause Tyk to apply both policies to the session object: - -```json -{ - "sub": "1234567890", - "name": "Alice Smith", - "accessScopes": ["read:users", "write:users"] -} -``` - -#### Combining policies - -Where multiple policies are mapped to a session (for example, if several scopes are declared in the JWT claim, or if you set multiple *default policies*) Tyk will apply all the matching policies to the request, combining their access rights and using the most permissive rate limits and quotas. It's important when creating those policies to ensure that they do not conflict with each other. - -Policies are combined as follows: - -1. Apply direct mapped policies declared via `basePolicyClaims` -2. Apply scope mapped policies declared in `scopeToPolicyMapping` based upon scopes in the JWT -3. If no policies have been applied in steps 1 or 2, apply the default policies from `defaultPolicies` - -When multiple policies are combined the following logic is applied: - -- **access rights** A user gets access to an endpoint if ANY of the applied policies grant access -- **rate limits** Tyk uses the most permissive values (highest quota, lowest rate limit) -- **other settings** The most permissive settings from any policy are applied - -#### Policy Best Practices - -When creating multiple policies that might be applied to the same JWT, we recommend using [partitioned policies]({{< ref "api-management/policies#partitioned-policies" >}}) - policies that control specific aspects of API access rather than trying to configure everything in a single policy. - -For example: - -- Create one policy that grants read-only access to specific endpoints -- Create another policy that grants write access to different endpoints -- Create a third policy that sets specific rate limits - -To ensure these policies work correctly when combined: - -- Set `per_api` to `true` in each policy. This ensures that the policy's settings only apply to the specific APIs listed in that policy, not to all APIs globally. -- Avoid listing the same `API ID` in multiple policies with conflicting settings. Instead, create distinct policies with complementary settings that can be safely combined. - - -### Session Updates - -When a JWT's claims change (for example, configuring different scopes or policies), Tyk will update the session with the new policies on the next request made with the token. - -### Missing Policies - -If a policy Id is mapped to a session, but there is no policy with that Id, Tyk will fail safe and reject the request returning the `HTTP 403 Forbidden` response with `Key not authorized: no matching policy`. Tyk Gateway will also log the error: `Policy ID found is invalid!`. - -### Using Multiple Identity Providers - -When using multiple Identity Providers, you may need to check different claim locations for the same information. Tyk supports multiple claim locations for the subject identity and policy Ids. - -Prior to Tyk 5.10 and for Tyk Classic APIs, the Gateway could be configured to check single claims for the subject identity, base policy and scope-to-policy mapping. This did not support the scenario where different IdPs used different claims - for example, for the policy mapping, Keycloak uses `scope`, whereas Okta uses `scp`. - -From Tyk 5.10+, Tyk OAS APIs can be configured to check multiple claim names to locate these data in the received token. - -| API Configuration Type | Tyk Version | Subject Identity Locator | Base Policy Locator | Scope-to-Policy Mapping Locator | -|-------------|----------|---|---|---| -| Tyk OAS | pre-5.10 | `identityBaseField` | `policyFieldName` | `scopes.claimName` | -| Tyk OAS | 5.10+ | `subjectClaims` | `basePolicyClaims` | `scopes.claims`| -| Tyk Classic | all | `jwt_identity_base_field` | `jwt_policy_field_name` | `jwt_scope_claim_name` | - -For example: - -```yaml -x-tyk-api-gateway: - server: - authentication: - securitySchemes: - jwtAuth: - # Legacy single field (still supported) - identityBaseField: "sub" - - # New multi-location support (Tyk 5.10+) - subjectClaims: - - "sub" - - "username" - - "user_id" -``` - ## Split Token Flow Split Token Flow addresses a fundamental security concern with JWT tokens: when a JWT is stored on a client device (browser, mobile app, etc.), all of its contents can be easily decoded since JWTs are only base64-encoded, not encrypted. This means sensitive information in the payload is potentially exposed. diff --git a/tyk-docs/content/developer-support/release-notes/gateway.md b/tyk-docs/content/developer-support/release-notes/gateway.md index d756639e43..0e8ba00fd3 100644 --- a/tyk-docs/content/developer-support/release-notes/gateway.md +++ b/tyk-docs/content/developer-support/release-notes/gateway.md @@ -106,7 +106,7 @@ This enhancement makes Tyk's JWT middleware the primary validation mechanism for Ideal for organizations that require sophisticated JWT validation beyond standard token checks. -For more details, please see the dedicated [JWT Auth]({{< ref "basic-config-and-security/security/authentication-authorization/json-web-tokens#managing-authorization-with-jwt" >}}) section. +For more details, please see the dedicated [JWT Auth]({{< ref "api-management/authentication/jwt-authorization" >}}) section. ##### Advanced JWKS Cache Management for Tyk OAS APIs @@ -125,7 +125,7 @@ Tyk Gateway now provides comprehensive JWKS (JSON Web Key Set) cache control for This enhancement is particularly valuable for organizations migrating to Tyk OAS APIs or those requiring consistent low-latency JWT validation performance with multiple Identity Providers that have different key rotation policies. -For more details, please see the [JWT Auth]({{< ref "basic-config-and-security/security/authentication-authorization/json-web-tokens#jwt-signatures" >}}) section. +For more details, please see the [JWT Auth]({{< ref "basic-config-and-security/security/authentication-authorization/json-web-tokens#signature-validation" >}}) section. ##### Centralized External Service Configuration diff --git a/tyk-docs/content/tyk-stack/tyk-operator/create-an-api.md b/tyk-docs/content/tyk-stack/tyk-operator/create-an-api.md index 1665b078c4..f03bf30b18 100644 --- a/tyk-docs/content/tyk-stack/tyk-operator/create-an-api.md +++ b/tyk-docs/content/tyk-stack/tyk-operator/create-an-api.md @@ -739,7 +739,7 @@ This configuration uses [JWT tokens]({{< ref "basic-config-and-security/security Users can configure JWT authentication by defining the following fields: -- `jwt_signing_method`: Specify the method used to sign the JWT. Refer to the documentation on [JWT Signatures]({{< ref "basic-config-and-security/security/authentication-authorization/json-web-tokens#jwt-signatures">}}) for supported methods. +- `jwt_signing_method`: Specify the method used to sign the JWT. Refer to the documentation on [JWT Signatures]({{< ref "basic-config-and-security/security/authentication-authorization/json-web-tokens#signature-validation">}}) for supported methods. - `jwt_source`: Specify the public key used for verifying the JWT. - `jwt_identity_base_field`: Define the identity source, typically set to `sub` (subject), which uniquely identifies the user or entity. - `jwt_policy_field_name`: Specify the claim within the JWT payload that indicates the policy ID to apply. diff --git a/tyk-docs/data/menu.yaml b/tyk-docs/data/menu.yaml index 7c1a34e2f4..65e0bb7542 100644 --- a/tyk-docs/data/menu.yaml +++ b/tyk-docs/data/menu.yaml @@ -657,9 +657,21 @@ menu: category: Page show: True - title: "JSON Web Tokens" - path: /basic-config-and-security/security/authentication-authorization/json-web-tokens - category: Page + category: Directory show: True + menu: + - title: "Overview" + path: /basic-config-and-security/security/authentication-authorization/json-web-tokens + category: Page + show: True + - title: "Claim Validation" + path: /api-management/authentication/jwt-claim-validation + category: Page + show: True + - title: "Authorization" + path: /api-management/authentication/jwt-authorization + category: Page + show: True - title: "HMAC Signatures" path: /basic-config-and-security/security/authentication-authorization/hmac-signatures category: Page