feat: implement configurable concurrent session limits per user and client#3964
Open
mshipkovenski wants to merge 2 commits into
Open
feat: implement configurable concurrent session limits per user and client#3964mshipkovenski wants to merge 2 commits into
mshipkovenski wants to merge 2 commits into
Conversation
|
|
…lient
Introduce numeric concurrent session limits for refresh tokens, replacing
the previous binary refreshTokenUnique flag with a numeric cap.
Key changes:
- TokenPolicy: the internal `refreshTokenUnique` field is now an `int`
(positive value = max sessions, -1 = unlimited). The original boolean
getters/setters (`isRefreshTokenUnique` / `setRefreshTokenUnique`) are
preserved with @JsonIgnore for backwards compatibility. New
`getMaxSessionLimit` / `setMaxSessionLimit` accessors operate on the
integer value. The @JsonSetter accepts boolean or integer input so
existing configurations continue to work.
- ClientConstants: adds a REFRESH_TOKEN_UNIQUE constant for the
additionalInformation key used for per-client overrides.
- OauthEndpointBeanConfiguration: the `jwt.token.refresh.unique` property
now accepts both boolean strings ("true"/"false") and positive integers,
parsed at startup.
- UaaTokenServices: replaces the all-or-nothing
`deleteRefreshTokensForClientAndUserId` call with
`enforceConcurrentSessionLimit`, which revokes only the oldest tokens
when the active count would exceed the configured limit. A per-client
override (stored in additionalInformation) takes precedence over the
zone-level policy. Rotation-aware: token reuse and token replacement are
detected to avoid counting a refresh as a new session.
- SampleIdentityZone.json: `refreshTokenUnique` serialises as -1 (integer)
instead of false to reflect the new type.
- Tests: DeprecatedUaaTokenServicesTests, RefreshRotationTest, TokenPolicyTest,
and the IdentityZone endpoint tests are updated to cover the new behaviour,
including per-user isolation, public-client rotation, and confidential-client
JTI-reuse scenarios.
Co-authored-by: Cursor <cursoragent@cursor.com>
ai-assisted=yes
db894b6 to
593b9e1
Compare
Contributor
|
Can you please update the configuration reference too: https://github.com/cloudfoundry/uaa/blob/develop/docs/UAA-Configuration-Reference.md#jwttokenrefreshunique |
The property now accepts a boolean or a positive integer. Update the configuration reference to document: - Type change from boolean to boolean | integer - Semantics: false/0 = no limit, true = 1 session, N = N concurrent sessions - Prerequisite: jwt.token.revocable must also be true - Per-client override via refreshTokenUnique in additionalInformation - Example yaml snippet showing a limit of 2 concurrent sessions - Links to related properties jwt.token.revocable and jwt.token.refresh.rotate Co-authored-by: Cursor <cursoragent@cursor.com> ai-assisted=yes
Author
Done |
Comment on lines
+130
to
133
| @JsonIgnore | ||
| public boolean isRefreshTokenUnique() { | ||
| return refreshTokenUnique; | ||
| return refreshTokenUnique > 0; | ||
| } |
Comment on lines
64
to
70
| import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; | ||
| import org.cloudfoundry.identity.uaa.zone.MultitenantClientServices; | ||
| import org.cloudfoundry.identity.uaa.zone.TokenPolicy; | ||
| import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; | ||
| import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManagerImpl; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; |
Comment on lines
+988
to
+993
| /** | ||
| * Enforces the per-user, per-client concurrent session limit by revoking the oldest refresh tokens so that | ||
| * at most {@code limit - 1} remain, leaving room for the refresh token that is about to be issued. A | ||
| * non-positive limit means unlimited and disables enforcement. This only takes effect when refresh tokens | ||
| * are revocable (i.e. {@code jwt.token.revocable=true}), which is guaranteed by the caller. | ||
| */ |
Comment on lines
+70
to
+73
| private static final String REFRESH_TOKEN_UNIQUE = "Maximum number of concurrent active refresh tokens (sessions) per user/client pair. " + | ||
| "Accepts a boolean or an integer for backwards compatibility: `true` is equivalent to `1` (one session), " + | ||
| "`false` or any non-positive value means no limit. A positive integer N allows up to N concurrent sessions. " + | ||
| "Only enforced when `jwtRevocable` is also `true`. Defaults to `false` (no limit)."; |
Comment on lines
+1056
to
+1058
| When the limit is reached, the **oldest** refresh token is revoked to make room for the new | ||
| one. Enforcement only takes effect when `jwt.token.revocable` is also `true` (revocable | ||
| tokens must be enabled so UAA can track and delete them). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Introduce numeric concurrent session limits for refresh tokens, replacing the previous binary refreshTokenUnique flag with a numeric cap.
Key changes:
TokenPolicy: the internal
refreshTokenUniquefield is now anint(positive value = max sessions, -1 = unlimited). The original boolean getters/setters (isRefreshTokenUnique/setRefreshTokenUnique) are preserved with @JsonIgnore for backwards compatibility. NewgetMaxSessionLimit/setMaxSessionLimitaccessors operate on the integer value. The @JsonSetter accepts boolean or integer input so existing configurations continue to work.ClientConstants: adds a REFRESH_TOKEN_UNIQUE constant for the additionalInformation key used for per-client overrides.
OauthEndpointBeanConfiguration: the
jwt.token.refresh.uniqueproperty now accepts both boolean strings ("true"/"false") and positive integers, parsed at startup.UaaTokenServices: replaces the all-or-nothing
deleteRefreshTokensForClientAndUserIdcall withenforceConcurrentSessionLimit, which revokes only the oldest tokens when the active count would exceed the configured limit. A per-client override (stored in additionalInformation) takes precedence over the zone-level policy. Rotation-aware: token reuse and token replacement are detected to avoid counting a refresh as a new session.SampleIdentityZone.json:
refreshTokenUniqueserialises as -1 (integer) instead of false to reflect the new type.Tests: DeprecatedUaaTokenServicesTests, RefreshRotationTest, TokenPolicyTest, and the IdentityZone endpoint tests are updated to cover the new behaviour, including per-user isolation, public-client rotation, and confidential-client JTI-reuse scenarios.