You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@knox.apache.org by "Sandor Molnar (Jira)" <ji...@apache.org> on 2026/10/22 13:21:00 UTC

[jira] [Updated] (KNOX-3529) Count the per-user token limit separately for each token type

     [ https://issues.apache.org/jira/browse/KNOX-3529?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Sandor Molnar updated KNOX-3529:
--------------------------------
    Description: 
h3. Problem

The per-user token limit ({{knox.token.limit.per.user}}) currently counts every token stored for a user, regardless of type, except KnoxSSO cookies ({{TokenResource#enforceTokenLimitsAsRequired}}). The token state service is shared across the whole gateway, so tokens issued by one service (or topology) count against the limits of all the others.

This causes trouble when a client uses one token type to get another. Example:
- Topology A runs the {{CLIENTID}} service with {{clientid.knox.token.limit.per.user=100}}. A user creates 10 client credentials there.
- Topology B runs the {{KNOXTOKEN}} service with {{knox.token.limit.per.user=150}}. A client exchanges those credentials for access tokens via {{/oauth/v1/token}}.
- Topology B can mint only 140 access tokens, because the 10 client credentials count against its limit too. API keys owned by the same user reduce it further.
- With {{knox.token.user.limit.exceeded.action=REMOVE_OLDEST}} it's worse: the oldest tokens are usually the long-lived client credentials or API keys, so minting an access token revokes the credential that was used to get it.

Client credentials and API keys are long-lived credentials. Access tokens issued by {{KNOXTOKEN}} are usually short-lived. Counting them together means neither limit means what an admin expects.

h3. Proposal

Count tokens only against the limit for their own type. Every stored token already records its type ({{TokenMetadata.TYPE}}: {{JWT}}, {{CLIENT_ID}}, {{API_KEY}}, {{KNOXSSO_COOKIE}}, ...), and {{TokenStateService#getTokens(userName)}} already returns that metadata, so no storage change is needed.

* {{TokenResource}} gets a hook that returns the token types its limit counts. The default is everything except {{KNOXSSO_COOKIE}}, {{CLIENT_ID}} and {{API_KEY}} (so {{JWT}}, {{AUTH_CODE}} and {{REFRESH_TOKEN}}).
- {{ClientCredentialsResource}} counts only {{CLIENT_ID}} tokens, and {{APIKeyResource}} counts only {{API_KEY}} tokens.
- {{enforceTokenLimitsAsRequired}} filters the user's tokens by that hook before comparing against the limit. {{REMOVE_OLDEST}} then revokes the oldest token of the same type only.
- Tokens with no {{TYPE}} metadata (stored by older versions) are treated as {{JWT}}.

This changes what the limit means. Counting each token type separately becomes the default, controlled by a new service parameter that defaults to {{true}}. Deployments that want to keep today's behavior, where all of a user's tokens count together, can set it to {{false}}:

{code:xml}
<param>
    <name>knox.token.limit.per.user.by.type.enabled</name>
    <value>false</value>
</param>
{code}

For {{CLIENTID}} and {{APIKEY}}, the parameter takes the usual prefix ({{clientid.}} / {{apikey.}}).

Note: existing deployments will allow more tokens per user after upgrading, because each API now has its own budget instead of sharing one combined cap. For example, a user could have up to {{knox.token.limit.per.user}} JWTs plus that many client credentials, where before the two together were capped at that number. This is documented as an upgrade note.


h3. KnoxIDF tokens

KnoxIDF auth codes ({{AUTH_CODE}}) and refresh tokens ({{REFRESH_TOKEN}}) stay in the default count, together with {{JWT}}. They are short-lived and deleted on use (auth codes are consumed on redemption, refresh tokens on rotation), so they don't build up the way long-lived client credentials and API keys do. Excluding them would also leave them with no per-user limit at all.

  was:
h3. Problem

The per-user token limit ({{knox.token.limit.per.user}}) currently counts every token stored for a user, regardless of type, except KnoxSSO cookies ({{TokenResource#enforceTokenLimitsAsRequired}}). The token state service is shared across the whole gateway, so tokens issued by one service (or topology) count against the limits of all the others.

This causes trouble when a client uses one token type to get another. Example:
- Topology A runs the {{CLIENTID}} service with {{clientid.knox.token.limit.per.user=100}}. A user creates 10 client credentials there.
- Topology B runs the {{KNOXTOKEN}} service with {{knox.token.limit.per.user=150}}. A client exchanges those credentials for access tokens via {{/oauth/v1/token}}.
- Topology B can mint only 140 access tokens, because the 10 client credentials count against its limit too. API keys owned by the same user reduce it further.
- With {{knox.token.user.limit.exceeded.action=REMOVE_OLDEST}} it's worse: the oldest tokens are usually the long-lived client credentials or API keys, so minting an access token revokes the credential that was used to get it.

Client credentials and API keys are long-lived credentials. Access tokens issued by {{KNOXTOKEN}} are usually short-lived. Counting them together means neither limit means what an admin expects.

h3. Proposal

Count tokens only against the limit for their own type. Every stored token already records its type ({{TokenMetadata.TYPE}}: {{JWT}}, {{CLIENT_ID}}, {{API_KEY}}, {{KNOXSSO_COOKIE}}, ...), and {{TokenStateService#getTokens(userName)}} already returns that metadata, so no storage change is needed.

* {{TokenResource}} gets a hook that returns the token types its limit counts. The default is everything except {{KNOXSSO_COOKIE}}, {{CLIENT_ID}} and {{API_KEY}} (so {{JWT}}, {{AUTH_CODE}} and {{REFRESH_TOKEN}}).
- {{ClientCredentialsResource}} counts only {{CLIENT_ID}} tokens, and {{APIKeyResource}} counts only {{API_KEY}} tokens.
- {{enforceTokenLimitsAsRequired}} filters the user's tokens by that hook before comparing against the limit. {{REMOVE_OLDEST}} then revokes the oldest token of the same type only.
- Tokens with no {{TYPE}} metadata (stored by older versions) are treated as {{JWT}}.

This changes what the limit means, so it should be opt-in through a new service parameter that keeps today's behavior by default:
{code:xml}
<param>
    <name>knox.token.limit.per.user.by.type</name>
    <value>true</value>
</param>
{code}

h3. KnoxIDF tokens

KnoxIDF auth codes ({{AUTH_CODE}}) and refresh tokens ({{REFRESH_TOKEN}}) stay in the default count, together with {{JWT}}. They are short-lived and deleted on use (auth codes are consumed on redemption, refresh tokens on rotation), so they don't build up the way long-lived client credentials and API keys do. Excluding them would also leave them with no per-user limit at all.


> Count the per-user token limit separately for each token type
> -------------------------------------------------------------
>
>                 Key: KNOX-3529
>                 URL: https://issues.apache.org/jira/browse/KNOX-3529
>             Project: Apache Knox
>          Issue Type: Improvement
>          Components: JWT, Server
>    Affects Versions: 2.0.0, 2.1.0, 3.0.0
>            Reporter: Sandor Molnar
>            Assignee: Sandor Molnar
>            Priority: Major
>             Fix For: 3.1.0
>
>
> h3. Problem
> The per-user token limit ({{knox.token.limit.per.user}}) currently counts every token stored for a user, regardless of type, except KnoxSSO cookies ({{TokenResource#enforceTokenLimitsAsRequired}}). The token state service is shared across the whole gateway, so tokens issued by one service (or topology) count against the limits of all the others.
> This causes trouble when a client uses one token type to get another. Example:
> - Topology A runs the {{CLIENTID}} service with {{clientid.knox.token.limit.per.user=100}}. A user creates 10 client credentials there.
> - Topology B runs the {{KNOXTOKEN}} service with {{knox.token.limit.per.user=150}}. A client exchanges those credentials for access tokens via {{/oauth/v1/token}}.
> - Topology B can mint only 140 access tokens, because the 10 client credentials count against its limit too. API keys owned by the same user reduce it further.
> - With {{knox.token.user.limit.exceeded.action=REMOVE_OLDEST}} it's worse: the oldest tokens are usually the long-lived client credentials or API keys, so minting an access token revokes the credential that was used to get it.
> Client credentials and API keys are long-lived credentials. Access tokens issued by {{KNOXTOKEN}} are usually short-lived. Counting them together means neither limit means what an admin expects.
> h3. Proposal
> Count tokens only against the limit for their own type. Every stored token already records its type ({{TokenMetadata.TYPE}}: {{JWT}}, {{CLIENT_ID}}, {{API_KEY}}, {{KNOXSSO_COOKIE}}, ...), and {{TokenStateService#getTokens(userName)}} already returns that metadata, so no storage change is needed.
> * {{TokenResource}} gets a hook that returns the token types its limit counts. The default is everything except {{KNOXSSO_COOKIE}}, {{CLIENT_ID}} and {{API_KEY}} (so {{JWT}}, {{AUTH_CODE}} and {{REFRESH_TOKEN}}).
> - {{ClientCredentialsResource}} counts only {{CLIENT_ID}} tokens, and {{APIKeyResource}} counts only {{API_KEY}} tokens.
> - {{enforceTokenLimitsAsRequired}} filters the user's tokens by that hook before comparing against the limit. {{REMOVE_OLDEST}} then revokes the oldest token of the same type only.
> - Tokens with no {{TYPE}} metadata (stored by older versions) are treated as {{JWT}}.
> This changes what the limit means. Counting each token type separately becomes the default, controlled by a new service parameter that defaults to {{true}}. Deployments that want to keep today's behavior, where all of a user's tokens count together, can set it to {{false}}:
> {code:xml}
> <param>
>     <name>knox.token.limit.per.user.by.type.enabled</name>
>     <value>false</value>
> </param>
> {code}
> For {{CLIENTID}} and {{APIKEY}}, the parameter takes the usual prefix ({{clientid.}} / {{apikey.}}).
> Note: existing deployments will allow more tokens per user after upgrading, because each API now has its own budget instead of sharing one combined cap. For example, a user could have up to {{knox.token.limit.per.user}} JWTs plus that many client credentials, where before the two together were capped at that number. This is documented as an upgrade note.
> h3. KnoxIDF tokens
> KnoxIDF auth codes ({{AUTH_CODE}}) and refresh tokens ({{REFRESH_TOKEN}}) stay in the default count, together with {{JWT}}. They are short-lived and deleted on use (auth codes are consumed on redemption, refresh tokens on rotation), so they don't build up the way long-lived client credentials and API keys do. Excluding them would also leave them with no per-user limit at all.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)