From ecd1505fcb6a14d0be26d22d13d0c7136f5f8b0d Mon Sep 17 00:00:00 2001 From: Milinda Dias Date: Mon, 22 Sep 2025 21:23:39 +0530 Subject: [PATCH 1/4] feat: documentation --- .../authentication-and-authorization.mdx | 20 +++++++++++++++++++ docs/router/configuration.mdx | 14 +++++++++++++ 2 files changed, 34 insertions(+) diff --git a/docs/router/authentication-and-authorization.mdx b/docs/router/authentication-and-authorization.mdx index 495c11c3..e46c048a 100644 --- a/docs/router/authentication-and-authorization.mdx +++ b/docs/router/authentication-and-authorization.mdx @@ -26,6 +26,11 @@ In the current router version, the configuration and behavior of authentication - url: https://example.com/.well-known/jwks.json refresh_interval: 1m algorithms: ["RS256"] + refresh_unknown_kid: + enabled: true + max_wait: 2m + interval: 30s + burst: 5 - url: https://example2.com/.well-known/jwks.json refresh_interval: 2m # optional list of allowed algorithms per JWKS @@ -89,3 +94,18 @@ Authentication information is also available to custom modules. See [Access Auth ### Forwarding authentication headers By default, the router won't forward authentication headers to subgraphs, but if desired this can be configured using [Proxy capabilities](/router/proxy-capabilities). + + +### Refreshing Unknown KIDs on demand +Setting `refresh_unknown_kid.enabled` as `true`, would enable the automatic refreshing of KIDs whenever a valid token with an unknwon KID is passed to the router. The router will assume that the JWKs has not been rotated and will call the JWKs endpoint hoping the JWKs with the matching KID has been added. However to prevent overloading the JWKs endpoint we ratelimit this feature, ratelimiting works based on the "burst" value specified. + +Let's say that the burst value was 1 and we sent 6 requests, this means after the 1st request, we need to wait 30 seconds (the value of `refresh_unknown_kid.interval`) before sending the next request with an unknown KID. If we however do end up sending another request with an unknown KID before the duration has elapsed, the response of that request will be blocked until it has elapsed. Likewise, since we can process only one request at a time, if we send 6 requests simultaneously, with an interval of `30s` + +The 2nd request will block for 30s (30s * 1) +The 3rd request will block for 60s (30s * 2) +The 4th request will block for 90s (30s * 3) +The 5th request will block for 120s (30s * 4) +The 6th request will block for 150s (30s * 5) + +The `max_wait` value, is used to prevent the ballooning of waits. If the max wait was set to `110s`, for the 5th and 6th requests, we already can compute and know that they will be blocked for more than `110s`, as such we immediately stop searching for unknown KIDs and error out. + diff --git a/docs/router/configuration.mdx b/docs/router/configuration.mdx index 2eba810c..42327e6b 100644 --- a/docs/router/configuration.mdx +++ b/docs/router/configuration.mdx @@ -1428,6 +1428,10 @@ This is useful when you want to connect to a JWKS endpoint | url | | The URL of the JWKs. The JWKs are used to verify the JWT (JSON Web Token). The URL is specified as a string with the format 'scheme://host:port'. | | | refresh_interval | | The interval at which the JWKs are refreshed. The period is specified as a string with a number and a unit, e.g. 10ms, 1s, 1m, 1h. The supported units are 'ms', 's', 'm', 'h'. | 1m | | algorithms | | The allowed algorithms for the keys that are retrieved from the JWKs. An empty list means that all algorithms are allowed. The following algorithms are supported "RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "PS256", "PS384", "PS512", "EdDSA" | [] (all allowed) | +| refresh_unknown_kid.enabled | | Enable refreshing JWKs when encountering unknown key IDs | false | +| refresh_unknown_kid.max_wait | | Maximum time allowed for rate limiting refreshing unknown KIDs, if the wait duration computed is greater than the max duration, an error would be returned immediately | 2m | +| refresh_unknown_kid.interval | | Time interval between allowed JWKs refreshes when encountering unknown KIDs | 30s | +| refresh_unknown_kid.burst | | Number of maximum concurrent JWKs refresh requests allowed when encountering unknown KIDs | 2 | #### Secret This is useful when you have a symmetric key that you cannot expose through a JWKS endpoint, you can use the secret based configuration @@ -1472,10 +1476,20 @@ authentication: - url: "https://example.com/.well-known/jwks.json" refresh_interval: 1m # Leaving algorithms empty will allow all supported algorithms from the config docs + refresh_unknown_kid: + enabled: true + max_wait: 2m + interval: 30s + burst: 2 - url: "https://example2.com/.well-known/jwks.json" refresh_interval: 2m # optional list of allowed algorithms per JWKS algorithms: ["RS256", "EdDSA", "HS512"] + refresh_unknown_kid: + enabled: false # These are defaults + max_wait: 2m + interval: 30s + burst: 2 header_name: Authorization # This is the default value header_value_prefix: Bearer # This is the default value header_sources: From cf084f3093ebecfce985db5c0304297a06f3f875 Mon Sep 17 00:00:00 2001 From: Milinda Dias Date: Mon, 22 Sep 2025 21:26:01 +0530 Subject: [PATCH 2/4] feat: documentation --- .../authentication-and-authorization.mdx | 41 +++++++++++++------ docs/router/configuration.mdx | 8 ++-- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/docs/router/authentication-and-authorization.mdx b/docs/router/authentication-and-authorization.mdx index e46c048a..34c33c39 100644 --- a/docs/router/authentication-and-authorization.mdx +++ b/docs/router/authentication-and-authorization.mdx @@ -96,16 +96,33 @@ Authentication information is also available to custom modules. See [Access Auth By default, the router won't forward authentication headers to subgraphs, but if desired this can be configured using [Proxy capabilities](/router/proxy-capabilities). -### Refreshing Unknown KIDs on demand -Setting `refresh_unknown_kid.enabled` as `true`, would enable the automatic refreshing of KIDs whenever a valid token with an unknwon KID is passed to the router. The router will assume that the JWKs has not been rotated and will call the JWKs endpoint hoping the JWKs with the matching KID has been added. However to prevent overloading the JWKs endpoint we ratelimit this feature, ratelimiting works based on the "burst" value specified. - -Let's say that the burst value was 1 and we sent 6 requests, this means after the 1st request, we need to wait 30 seconds (the value of `refresh_unknown_kid.interval`) before sending the next request with an unknown KID. If we however do end up sending another request with an unknown KID before the duration has elapsed, the response of that request will be blocked until it has elapsed. Likewise, since we can process only one request at a time, if we send 6 requests simultaneously, with an interval of `30s` - -The 2nd request will block for 30s (30s * 1) -The 3rd request will block for 60s (30s * 2) -The 4th request will block for 90s (30s * 3) -The 5th request will block for 120s (30s * 4) -The 6th request will block for 150s (30s * 5) - -The `max_wait` value, is used to prevent the ballooning of waits. If the max wait was set to `110s`, for the 5th and 6th requests, we already can compute and know that they will be blocked for more than `110s`, as such we immediately stop searching for unknown KIDs and error out. +### Refreshing Unknown KIDs on Demand + +When `refresh_unknown_kid.enabled` is set to `true`, the router will automatically attempt to refresh JWKs whenever it encounters a valid token with an unknown KID (Key ID). This is useful when JWKs rotation has occurred but the router hasn't picked up the new keys in its regular refresh cycle. + +To prevent overwhelming the JWKS endpoint, this feature includes rate limiting controlled by the following parameters: +- `burst`: Maximum number of concurrent refresh attempts allowed +- `interval`: Time that must pass between consecutive refresh attempts +- `max_wait`: Maximum time a request is allowed to wait + +#### Rate Limiting Example + +Let's examine how rate limiting works with these settings: +```yaml +refresh_unknown_kid: + enabled: true + burst: 1 + interval: 30s + max_wait: 110s +``` + +If we send 6 simultaneous requests with unknown KIDs: +1. First request: Processed immediately +2. Second request: Blocked for 30s (interval × 1) +3. Third request: Blocked for 60s (interval × 2) +4. Fourth request: Blocked for 90s (interval × 3) +5. Fifth request: Returns error immediately (would require 120s wait > max_wait) +6. Sixth request: Returns error immediately (would require 150s wait > max_wait) + +The `max_wait` setting prevents excessive wait times. In this example, since the 5th and 6th requests would require waiting longer than the configured `max_wait` of 110s, they immediately return with an unauthorized status instead of attempting to refresh. diff --git a/docs/router/configuration.mdx b/docs/router/configuration.mdx index 42327e6b..681c231e 100644 --- a/docs/router/configuration.mdx +++ b/docs/router/configuration.mdx @@ -1428,10 +1428,10 @@ This is useful when you want to connect to a JWKS endpoint | url | | The URL of the JWKs. The JWKs are used to verify the JWT (JSON Web Token). The URL is specified as a string with the format 'scheme://host:port'. | | | refresh_interval | | The interval at which the JWKs are refreshed. The period is specified as a string with a number and a unit, e.g. 10ms, 1s, 1m, 1h. The supported units are 'ms', 's', 'm', 'h'. | 1m | | algorithms | | The allowed algorithms for the keys that are retrieved from the JWKs. An empty list means that all algorithms are allowed. The following algorithms are supported "RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "PS256", "PS384", "PS512", "EdDSA" | [] (all allowed) | -| refresh_unknown_kid.enabled | | Enable refreshing JWKs when encountering unknown key IDs | false | -| refresh_unknown_kid.max_wait | | Maximum time allowed for rate limiting refreshing unknown KIDs, if the wait duration computed is greater than the max duration, an error would be returned immediately | 2m | -| refresh_unknown_kid.interval | | Time interval between allowed JWKs refreshes when encountering unknown KIDs | 30s | -| refresh_unknown_kid.burst | | Number of maximum concurrent JWKs refresh requests allowed when encountering unknown KIDs | 2 | +| refresh_unknown_kid.enabled | | Enable automatic refreshing of JWKs when encountering a valid token with an unknown KID. When enabled, the router will attempt to fetch updated JWKs hoping the matching KID has been added. | false | +| refresh_unknown_kid.max_wait | | Maximum wait time allowed for rate-limited requests. If the computed wait duration would exceed this value, the request returns immediately with an unauthorized status instead of waiting. This prevents excessive wait times for subsequent requests. | 2m | +| refresh_unknown_kid.interval | | Time interval that must pass between consecutive JWKs refresh attempts. With burst=1, each subsequent request within the interval must wait for (interval × attempt number) duration. | 30s | +| refresh_unknown_kid.burst | | Maximum number of concurrent refresh attempts allowed. This helps prevent overloading the JWKS endpoint. When exceeded, subsequent requests are queued and subject to rate limiting based on the interval. | 2 | #### Secret This is useful when you have a symmetric key that you cannot expose through a JWKS endpoint, you can use the secret based configuration From 9712e2c1bb11523d5fe4969f5d7f5189a7a0f229 Mon Sep 17 00:00:00 2001 From: Milinda Dias Date: Tue, 23 Sep 2025 22:38:14 +0530 Subject: [PATCH 3/4] fix: updates --- .../router/authentication-and-authorization.mdx | 17 ++++++++++------- docs/router/configuration.mdx | 8 ++++---- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/docs/router/authentication-and-authorization.mdx b/docs/router/authentication-and-authorization.mdx index 34c33c39..2da70715 100644 --- a/docs/router/authentication-and-authorization.mdx +++ b/docs/router/authentication-and-authorization.mdx @@ -101,9 +101,9 @@ By default, the router won't forward authentication headers to subgraphs, but if When `refresh_unknown_kid.enabled` is set to `true`, the router will automatically attempt to refresh JWKs whenever it encounters a valid token with an unknown KID (Key ID). This is useful when JWKs rotation has occurred but the router hasn't picked up the new keys in its regular refresh cycle. To prevent overwhelming the JWKS endpoint, this feature includes rate limiting controlled by the following parameters: -- `burst`: Maximum number of concurrent refresh attempts allowed -- `interval`: Time that must pass between consecutive refresh attempts -- `max_wait`: Maximum time a request is allowed to wait +- `burst`: Maximum number of refreshes allowed without being blocked immediately. Internally we keep a counter of how many `unused burst tokens` are available, whenever the `interval` elapses we increase the number of `unused burst tokens` by 1 (if the unused tokens does not exceed the limit set by `burst`). +- `interval`: The time that needs to elapse to increase the number of unused burst tokens, until the `burst` limit is reached. +- `max_wait`: Maximum time a refresh is allowed to wait #### Rate Limiting Example @@ -116,13 +116,16 @@ refresh_unknown_kid: max_wait: 110s ``` + If we send 6 simultaneous requests with unknown KIDs: -1. First request: Processed immediately -2. Second request: Blocked for 30s (interval × 1) -3. Third request: Blocked for 60s (interval × 2) -4. Fourth request: Blocked for 90s (interval × 3) +1. First request: JWKS refreshed immediately, 1 burst token is used up +2. Second request: Blocked for 30s (interval × 1), the burst token is freed after 30s +3. Third request: Blocked for 60s (interval × 2), the burst token is freed after another 30s +4. Fourth request: Blocked for 90s (interval × 3), the burst token is freed after another 30s 5. Fifth request: Returns error immediately (would require 120s wait > max_wait) 6. Sixth request: Returns error immediately (would require 150s wait > max_wait) +The 2nd, 3rd and 4th requests are blocked immediately because the burst capacity is set to 1, so after 1 request is sent through the number of `available burst tokens` is 0, the interval needs to elapse. If the burst was set to 2 for example, the second request would also be let through immediately. + The `max_wait` setting prevents excessive wait times. In this example, since the 5th and 6th requests would require waiting longer than the configured `max_wait` of 110s, they immediately return with an unauthorized status instead of attempting to refresh. diff --git a/docs/router/configuration.mdx b/docs/router/configuration.mdx index 681c231e..1dc10aaf 100644 --- a/docs/router/configuration.mdx +++ b/docs/router/configuration.mdx @@ -1428,10 +1428,10 @@ This is useful when you want to connect to a JWKS endpoint | url | | The URL of the JWKs. The JWKs are used to verify the JWT (JSON Web Token). The URL is specified as a string with the format 'scheme://host:port'. | | | refresh_interval | | The interval at which the JWKs are refreshed. The period is specified as a string with a number and a unit, e.g. 10ms, 1s, 1m, 1h. The supported units are 'ms', 's', 'm', 'h'. | 1m | | algorithms | | The allowed algorithms for the keys that are retrieved from the JWKs. An empty list means that all algorithms are allowed. The following algorithms are supported "RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "PS256", "PS384", "PS512", "EdDSA" | [] (all allowed) | -| refresh_unknown_kid.enabled | | Enable automatic refreshing of JWKs when encountering a valid token with an unknown KID. When enabled, the router will attempt to fetch updated JWKs hoping the matching KID has been added. | false | -| refresh_unknown_kid.max_wait | | Maximum wait time allowed for rate-limited requests. If the computed wait duration would exceed this value, the request returns immediately with an unauthorized status instead of waiting. This prevents excessive wait times for subsequent requests. | 2m | -| refresh_unknown_kid.interval | | Time interval that must pass between consecutive JWKs refresh attempts. With burst=1, each subsequent request within the interval must wait for (interval × attempt number) duration. | 30s | -| refresh_unknown_kid.burst | | Maximum number of concurrent refresh attempts allowed. This helps prevent overloading the JWKS endpoint. When exceeded, subsequent requests are queued and subject to rate limiting based on the interval. | 2 | +| refresh_unknown_kid.enabled | | Enable automatic refreshing of JWKs when encountering a valid token with an unknown KID. When enabled, the router will attempt to fetch updated JWKs hoping the matching KID has been added. | false | +| refresh_unknown_kid.max_wait | | Maximum wait time allowed for rate-limited requests. If the computed wait duration would exceed this value, the request returns immediately with an unauthorized status instead of waiting. This prevents excessive wait times for subsequent requests. | 2m | +| refresh_unknown_kid.interval | | Time interval that must pass between consecutive JWKs refresh attempts. With burst=1, each subsequent request within the interval must wait for a (interval × attempt number) duration. | 30s | +| refresh_unknown_kid.burst | | Maximum number of refresh attempts allowed before an interval needs to elapse. This helps prevent overloading the JWKS endpoint. When exceeded, subsequent requests are blocked until the interval has elapsed. | 2 | #### Secret This is useful when you have a symmetric key that you cannot expose through a JWKS endpoint, you can use the secret based configuration From 0eb324af5841de1ce7485eb59c79a4afbbfbdd11 Mon Sep 17 00:00:00 2001 From: Milinda Dias Date: Tue, 23 Sep 2025 23:01:38 +0530 Subject: [PATCH 4/4] fix: documentation --- .../authentication-and-authorization.mdx | 24 +++++++++---------- docs/router/configuration.mdx | 12 +++++----- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/router/authentication-and-authorization.mdx b/docs/router/authentication-and-authorization.mdx index 2da70715..b472bf56 100644 --- a/docs/router/authentication-and-authorization.mdx +++ b/docs/router/authentication-and-authorization.mdx @@ -98,12 +98,12 @@ By default, the router won't forward authentication headers to subgraphs, but if ### Refreshing Unknown KIDs on Demand -When `refresh_unknown_kid.enabled` is set to `true`, the router will automatically attempt to refresh JWKs whenever it encounters a valid token with an unknown KID (Key ID). This is useful when JWKs rotation has occurred but the router hasn't picked up the new keys in its regular refresh cycle. +When `refresh_unknown_kid.enabled` is set to `true`, the router will automatically attempt to refresh the JWKS (fetch updated keys) whenever it encounters a valid token with an unknown KID (Key ID). This is useful when key rotation has occurred but the router hasn't picked up the new keys in its regular refresh cycle. To prevent overwhelming the JWKS endpoint, this feature includes rate limiting controlled by the following parameters: -- `burst`: Maximum number of refreshes allowed without being blocked immediately. Internally we keep a counter of how many `unused burst tokens` are available, whenever the `interval` elapses we increase the number of `unused burst tokens` by 1 (if the unused tokens does not exceed the limit set by `burst`). -- `interval`: The time that needs to elapse to increase the number of unused burst tokens, until the `burst` limit is reached. -- `max_wait`: Maximum time a refresh is allowed to wait +- `burst`: Maximum number of refreshes allowed without waiting. Internally, the router keeps a counter of available burst tokens. Each time the `interval` elapses, one token is replenished, up to the `burst` limit. +- `interval`: The time that must elapse between replenishing burst tokens, until the `burst` limit is reached. +- `max_wait`: Maximum time a refresh is allowed to wait. If the computed wait would exceed this value, the request is rejected immediately with a 401 Unauthorized status instead of waiting. #### Rate Limiting Example @@ -118,14 +118,14 @@ refresh_unknown_kid: If we send 6 simultaneous requests with unknown KIDs: -1. First request: JWKS refreshed immediately, 1 burst token is used up -2. Second request: Blocked for 30s (interval × 1), the burst token is freed after 30s -3. Third request: Blocked for 60s (interval × 2), the burst token is freed after another 30s -4. Fourth request: Blocked for 90s (interval × 3), the burst token is freed after another 30s -5. Fifth request: Returns error immediately (would require 120s wait > max_wait) -6. Sixth request: Returns error immediately (would require 150s wait > max_wait) +1. First request: JWKS refreshed immediately; 1 burst token is consumed. +2. Second request: Waits for 30s (interval × 1); a token is replenished after 30s. +3. Third request: Waits for 60s (interval × 2); a token is replenished after another 30s. +4. Fourth request: Waits for 90s (interval × 3); a token is replenished after another 30s. +5. Fifth request: Fails immediately (would require 120s wait > max_wait) with 401 Unauthorized. +6. Sixth request: Fails immediately (would require 150s wait > max_wait) with 401 Unauthorized. -The 2nd, 3rd and 4th requests are blocked immediately because the burst capacity is set to 1, so after 1 request is sent through the number of `available burst tokens` is 0, the interval needs to elapse. If the burst was set to 2 for example, the second request would also be let through immediately. +The 2nd, 3rd and 4th requests are rate-limited because the burst capacity is set to 1. After the first request passes, the number of available burst tokens is 0 and subsequent requests must wait until the `interval` elapses. If `burst` were set to 2, the second request would also pass immediately. -The `max_wait` setting prevents excessive wait times. In this example, since the 5th and 6th requests would require waiting longer than the configured `max_wait` of 110s, they immediately return with an unauthorized status instead of attempting to refresh. +The `max_wait` setting prevents excessive wait times. In this example, since the 5th and 6th requests would require waiting longer than the configured `max_wait` of 110s, they immediately return with a 401 Unauthorized status instead of attempting to refresh. diff --git a/docs/router/configuration.mdx b/docs/router/configuration.mdx index 1dc10aaf..d3f8436b 100644 --- a/docs/router/configuration.mdx +++ b/docs/router/configuration.mdx @@ -1425,13 +1425,13 @@ This is useful when you want to connect to a JWKS endpoint | YAML | Required | Description | Default Value | | --------------------------------------------------------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -----------------| -| url | | The URL of the JWKs. The JWKs are used to verify the JWT (JSON Web Token). The URL is specified as a string with the format 'scheme://host:port'. | | -| refresh_interval | | The interval at which the JWKs are refreshed. The period is specified as a string with a number and a unit, e.g. 10ms, 1s, 1m, 1h. The supported units are 'ms', 's', 'm', 'h'. | 1m | +| url | | The URL of the JWKs. The JWKs are used to verify the JWT (JSON Web Token). The URL is specified as a string with the format 'scheme://host:port'. | | +| refresh_interval | | The interval at which the JWKs are refreshed. The period is specified as a string with a number and a unit, e.g. 10ms, 1s, 1m, 1h. The supported units are 'ms', 's', 'm', 'h'. | 1m | | algorithms | | The allowed algorithms for the keys that are retrieved from the JWKs. An empty list means that all algorithms are allowed. The following algorithms are supported "RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "PS256", "PS384", "PS512", "EdDSA" | [] (all allowed) | -| refresh_unknown_kid.enabled | | Enable automatic refreshing of JWKs when encountering a valid token with an unknown KID. When enabled, the router will attempt to fetch updated JWKs hoping the matching KID has been added. | false | -| refresh_unknown_kid.max_wait | | Maximum wait time allowed for rate-limited requests. If the computed wait duration would exceed this value, the request returns immediately with an unauthorized status instead of waiting. This prevents excessive wait times for subsequent requests. | 2m | -| refresh_unknown_kid.interval | | Time interval that must pass between consecutive JWKs refresh attempts. With burst=1, each subsequent request within the interval must wait for a (interval × attempt number) duration. | 30s | -| refresh_unknown_kid.burst | | Maximum number of refresh attempts allowed before an interval needs to elapse. This helps prevent overloading the JWKS endpoint. When exceeded, subsequent requests are blocked until the interval has elapsed. | 2 | +| refresh_unknown_kid.enabled | | Enable automatic JWKS refresh when encountering a valid token with an unknown KID (Key ID). When enabled, the router will fetch updated keys to find the matching KID. | false | +| refresh_unknown_kid.max_wait | | Maximum time a refresh is allowed to wait. If the computed wait would exceed this value, the request fails immediately with 401 Unauthorized instead of waiting. | 2m | +| refresh_unknown_kid.interval | | Time between replenishing burst tokens. With burst=1, each subsequent request within the interval must wait for (interval × attempt number). | 30s | +| refresh_unknown_kid.burst | | Maximum number of refreshes allowed without waiting. Prevents overloading the JWKS endpoint; beyond this, requests wait until the interval elapses or fail due to max_wait. | 2 | #### Secret This is useful when you have a symmetric key that you cannot expose through a JWKS endpoint, you can use the secret based configuration