From c100f53b4498cacdb4d1685d0b4b95e22f70f052 Mon Sep 17 00:00:00 2001 From: Andy Ost Date: Mon, 6 Oct 2025 15:42:55 +0100 Subject: [PATCH] Updated JWT claim section --- .../json-web-tokens.md | 792 +++++++++++++++++- 1 file changed, 746 insertions(+), 46 deletions(-) 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 f073c62f9c..fca875342b 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,21 +183,14 @@ 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-originator" >}}). +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" >}}). 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 `policyFieldName`) + - check for the value in the policy claim within the JWT (identified by the value stored in `basePolicyClaims`) - use this to identify the Tyk Security Policy (or policies) to be applied to the request - if there is no direct policy mapping, then the `defaultPolicy` will be used - apply the identified policies to the session, configuring access rights, rate limits and usage quota -### 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!`. ## Configuring your API to use JWT authentication @@ -234,7 +227,7 @@ x-tyk-api-gateway: You can optionally [strip the user credentials]({{< ref "api-management/client-authentication#managing-authorization-data" >}}) from the request prior to proxying to the upstream using the `authentication.stripAuthorizationData` field (Tyk Classic: `strip_auth_data`). -With the JWT method selected, you'll need to configure Tyk to handle the specific configuration of JSON Web Tokens that clients will be providing. All of the JWT specific configuration is performed within the [authentication.jwt]({{< ref "api-management/gateway-config-tyk-oas#jwt" >}}) object in the Tyk Vendor Extension. +With the JWT method selected, you'll need to configure Tyk to handle the specific configuration of JSON Web Tokens that clients will be providing. All of the JWT specific configuration is performed within the `authentication.jwt` object in the [Tyk Vendor Extension]({{< ref "api-management/gateway-config-tyk-oas#jwt" >}}). ### Locating the JWT in the Request @@ -261,7 +254,11 @@ x-tyk-api-gateway: name: cookie-auth ``` -### JWT Signatures +### Using Tyk Classic APIs + +As noted in the Tyk Classic API [documentation]({{< ref "api-management/gateway-config-tyk-classic#configuring-authentication-for-tyk-classic-apis" >}}), you can select JSON Web Token authentication using the `use_jwt` option. Tyk Classic APIs do not natively support multiple JWKS endpoints, though a [custom authentication plugin]({{< ref "api-management/plugins/plugin-types#authentication-plugins" >}}) could be used to implement this functionality. + +## Signature Validation | Method | Cryptographic Style | Secret Type | Supported Locations for Secret | Supported Algorithms | | --------- | ------------------- | ------------- | ------------------------------ | ---------------------------------------------------- | @@ -329,19 +326,87 @@ x-tyk-api-gateway: If both `.source` and `.jwksURIs` are configured, the latter will take precedence. {{< /note >}} -### JWT Validity and Clock Skew +## Claim Validation -JSON Web Tokens have a validity period described using three [Registered Claims](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1): `IssueAt`, `ExpireAt` and `NotBefore`. +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. -If, on receipt of the JWT, Tyk determines that the token is not valid then it will reject the request with the "Token is not valid yet" error. +Tyk supports validation of both: -Due to the nature of distributed systems it is expected that, despite best efforts, you can end up in a situation with clock skew between the party issuing the JWT (your IdP) and the validating party (Tyk). This might occur due to the clock on the Tyk server being behind or ahead of the clock on the Identity Provider server even with all servers synchronised from the same NTP server. +- **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. -You can optionally configure a maximum permissable difference between the timestamp in the token's validity claims and Tyk's clock, to allow for these scenarios. Each can be independently configured or omitted from the API configuration as required. The permissable skews are configured in seconds. +{{< 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" +} +``` -- **expiresAtValidationSkew** allows recently expired tokens to still be considered valid. This is useful when the token issuer's clock is slightly behind Tyk's clock. -- **issuedAtValidationSkew** allows tokens that claim to be issued slightly in the future to be valid. This helps when the token issuer's clock is slightly ahead of Tyk's clock. -- **notBeforeValidationSkew** allows tokens that claim to become valid slightly in the future to be valid now. This also helps when the token issuer's clock is ahead of Tyk's clock. +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: @@ -349,26 +414,613 @@ x-tyk-api-gateway: authentication: securitySchemes: jwtAuth: - issuedAtValidationSkew: 5 - notBeforeValidationSkew: 2 - expiresAtValidationSkew: 2 + 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 ``` -### Managing Authorization with JWT +- `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 -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. +{{< 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 -#### Identifying the Originator +**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. -Tyk follows a specific priority order when extracting identity from a JWT. If an empty identity is found, Tyk will move to the next step: +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 `skipKid` is enabled) -2. The subject identity claim identified by the value stored in `identityBaseField` (which allows API administrators to designate any JWT claim as the identity source (e.g., user_id, email, etc.). -3. The [Registered claim](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2") `sub` (which is the JWT specification's recommended claim for subject identity) +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 @@ -377,7 +1029,7 @@ When an identity has been determined, it is stored in the session object in thre 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 `identityBaseField` and determines that the value in the custom claim `user_id` within the JWT should be used as the identity for the session object. +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: @@ -386,10 +1038,10 @@ x-tyk-api-gateway: securitySchemes: jwtAuth: skipKid: true - identityBaseField: user_id + subjectClaims: [user_id] ``` -#### Identifying the Tyk Policies to be applied +### 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*. @@ -401,9 +1053,9 @@ Tyk supports three different types of policy mapping, which are applied in this 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 +#### 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 `policyFieldName` field in the API definition. +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: @@ -413,7 +1065,7 @@ x-tyk-api-gateway: authentication: securitySchemes: jwtAuth: - policyFieldName: pol + 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: @@ -422,7 +1074,13 @@ In the JWT, you should then provide the list of policy Ids as an array of values "pol": ["685a8af28c24bdac0dc21c28", "685bd90b8c24bd4b6d79443d"] ``` -##### Default policies +{{< 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. @@ -437,11 +1095,11 @@ x-tyk-api-gateway: - 685bd90b8c24bd4b6d79443d ``` -##### Scope policies +#### 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.claimName` to instruct Tyk where to look for the scopes and then you declare a mapping of scopes to policies within the `scopes.scopeToPolicyMapping` object. +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: @@ -455,12 +1113,18 @@ x-tyk-api-gateway: policyId: 685bd90b8c24bd4b6d79443d - scope: write:users policyId: 685a8af28c24bdac0dc21c28 - claimName: accessScopes + 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. -Multiple scopes can be declared within a JWT by setting the value of the claim in any of four configurations: +{{< 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"` @@ -471,7 +1135,7 @@ Multiple scopes can be declared within a JWT by setting the value of the claim i - 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 `claimName` so, for the first two examples above, the `claimName` should be set to `permissions` whilst for the the two nested examples you would use `permissions.access`. +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: @@ -483,13 +1147,13 @@ This example of a fragment of a JWT, if provided to an API with the configuratio } ``` -##### Combining policies +#### 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 `policyFieldName` +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` @@ -499,7 +1163,7 @@ When multiple policies are combined the following logic is applied: - **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 +#### 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. @@ -515,9 +1179,45 @@ To ensure these policies work correctly when combined: - Avoid listing the same `API ID` in multiple policies with conflicting settings. Instead, create distinct policies with complementary settings that can be safely combined. -### Using Tyk Classic APIs +### Session Updates -As noted in the Tyk Classic API [documentation]({{< ref "api-management/gateway-config-tyk-classic#configuring-authentication-for-tyk-classic-apis" >}}), you can select JSON Web Token authentication using the `use_jwt` option. Tyk Classic APIs do not natively support multiple JWKS endpoints, though a [custom authentication plugin]({{< ref "api-management/plugins/plugin-types#authentication-plugins" >}}) could be used to implement this functionality. +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