8000 Support for transient users for brokered accounts by hmlnarik · Pull Request #23977 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content

Support for transient users for brokered accounts - #23977

Merged
hmlnarik merged 7 commits into
keycloak:mainfrom
hmlnarik:11334-transient-sessions
Oct 25, 2023
Merged

hmlnarik merged 7 commits into
keycloak:mainfrom
hmlnarik:11334-transient-sessions

Conversation

@hmlnarik
@hmlnarik hmlnarik commented Oct 13, 2023 •
Copy link
Copy Markdown
Contributor

This PRototype adds capability of not importing brokered users into keycloak database, also known as transient users for brokering feature.

The core of the PR is that the user data is stored as a part of an authentication session (modifiable) and once authentication is done, it is stored in user session.

To do so, lightweight user model based on existing InMemoryUserAdapter class is introduced, and is de/serialized into a auth / user session attribute when needed.
An option is added in identity provider configuration to not store user into database. This option is disabled by default, keeping the behaviour backward compatible. When enabled, instead of creating a user in user database, a read/write lightweight user is created and its representation stored in authentication session. Once authentication finishes and user session is created, the user is stored as EDIT: read-write END object there.

This has several implications:

  • The authentication of the user relies purely on the trusted identity provider. For example, MFA on Keycloak side is not applicable (there is no place to store configuration).
  • The user is never stored in user database, thus it cannot be searched or updated. The user can still be viewed in admin console when navigating to it from client session of the respective client
  • EDIT: Since there is no persistent user, END the user roles and groups can only be assigned using mappers
    • Specifically, the user can view its account console as long as there is a mapper which maps respective roles in the identity provider settings
  • Removing the user session with lightweight user e.g. by logging out effectively removes the user
  • Every mapper of a transient identity provider effectively behaves as Import sync mode since it always treats the user as new. Hence sync mode settings is hidden in admin console identity provider screen when transient users are enabled

This PR is split into multiple commits:

  • The first commit is a visibility change which abstracts InMemoryUserAdapter so that it is visible both for services and model modules
  • Next commit adds the classes necessary for storing lightweight user representation, based on InMemoryUserAdapter
  • The third commit represents the core of the changes:
    • It modifies places where the user was added into database to account for the Do not store users settings, specifically instead of creating records in database, it creates lightweight users instead
    • Ensures that when a user was looked up from database, it can be reconstructed from auth / user session as needed
  • Next commit updates admin console functionality: Ensures that the user details can be viewed and identity provider can be configured accordingly
  • The fifth commit updates broker tests to account for transient session settings and adds specific test case for transient sessions

For this to become a full-blown PR, the approach needs to be confirmed and remaining tasks finished:

  • Documentation

cc: @keycloak/core

Closes: #11334

@cypress
cypress Bot commented Oct 13, 2023 •
Copy link
Copy Markdown

2 flaky tests on run #9554 ↗︎

0 527 48 0 Flakiness 2

Details:

Merge e18af88 into fb6af4d...
Project: Keycloak Admin UI Commit: 0c9ffc4b65 ℹ️
Status: Passed Duration: 12:13 💡
Started: Oct 24, 2023 12:49 PM Ended: Oct 24, 2023 1:01 PM
Flakiness  cypress/e2e/clients_test.spec.ts • 2 flaky tests • chrome

View Output Video

Test Artifacts
Clients test > Keys tab test > Generate new keys Test Replay Output Screenshots
Clients test > Accessibility tests for clients > Check a11y violations on load/ clients list tab Test Replay Output Screenshots

Review all test suite changes for PR #23977 ↗︎

@ghost ghost left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unreported flaky test detected, please review

@ghost
ghost commented Oct 13, 2023
Copy link
Copy Markdown

Unreported flaky test detected

If the below flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR.

org.keycloak.testsuite.ui.account2.LinkedAccountsTest#unlinkAccountTest

Keycloak CI - Account Console IT (firefox)

org.awaitility.core.ConditionTimeoutException: Assertion condition defined as a org.keycloak.testsuite.util.URLAssert URL expected to begin with: https://localhost:8543/auth/realms/test/protocol/openid-connect/auth ; actual URL: https://localhost:8543/auth/realms/test/account/#/security/linked-accounts within 10 seconds.
	at org.awaitility.core.ConditionAwaiter.await(ConditionAwaiter.java:167)
	at org.awaitility.core.AssertionCondition.await(AssertionCondition.java:119)
	at org.awaitility.core.AssertionCondition.await(AssertionCondition.java:31)
	at org.awaitility.core.ConditionFactory.until(ConditionFactory.java:985)
...

Report flaky test

org.keycloak.testsuite.ui.account2.WelcomeScreenTest#applicationsTest

Keycloak CI - Account Console IT (firefox)

org.awaitility.core.ConditionTimeoutException: Assertion condition defined as a org.keycloak.testsuite.util.URLAssert URL expected to begin with: https://localhost:8543/auth/realms/test/protocol/openid-connect/auth ; actual URL: https://localhost:8543/auth/realms/test/account/#/applications within 10 seconds.
	at org.awaitility.core.ConditionAwaiter.await(ConditionAwaiter.java:167)
	at org.awaitility.core.AssertionCondition.await(AssertionCondition.java:119)
	at org.awaitility.core.AssertionCondition.await(AssertionCondition.java:31)
	at org.awaitility.core.ConditionFactory.until(ConditionFactory.java:985)
...

Report flaky test

org.keycloak.testsuite.adapter.example.cors.CorsExampleAdapterTest#angularCorsProductTest

Keycloak CI - Base IT (2)

org.openqa.selenium.TimeoutException: 
Expected condition failed: waiting for text ('iphone') to be present in text ('') in element GrapheneElement -> [[ChromeDriver: chrome on LINUX (07db5e5062b898c89fda3e4c4910dd66)] -> id: output] (tried for 5 second(s) with 500 milliseconds interval)
Build info: version: '3.14.0', revision: 'aacccce0', time: '2018-08-02T20:19:58.91Z'
System info: host: 'fv-az167-188', ip: '10.1.0.41', os.name: 'Linux', os.arch: 'amd64', os.version: '6.2.0-1012-azure', java.version: '17.0.8.1'
Driver info: org.openqa.selenium.chrome.ChromeDriver$$EnhancerByGraphene$$73c7048c
...

Report flaky test

@hmlnarik
hmlnarik force-pushed the 11334-transient-sessions branch from c2ea1d5 to 7c97f1d Compare October 13, 2023 19:14
@ghost ghost left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unreported flaky test detected, please review

@ghost
ghost commented Oct 13, 2023
Copy link
Copy Markdown

Unreported flaky test detected

If the below flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR.

org.keycloak.testsuite.ui.account2.LinkedAccountsTest#unlinkAccountTest

Keycloak CI - Account Console IT (firefox)

org.awaitility.core.ConditionTimeoutException: Assertion condition defined as a org.keycloak.testsuite.util.URLAssert URL expected to begin with: https://localhost:8543/auth/realms/test/protocol/openid-connect/auth ; actual URL: https://localhost:8543/auth/realms/test/account/#/security/linked-accounts within 10 seconds.
	at org.awaitility.core.ConditionAwaiter.await(ConditionAwaiter.java:167)
	at org.awaitility.core.AssertionCondition.await(AssertionCondition.java:119)
	at org.awaitility.core.AssertionCondition.await(AssertionCondition.java:31)
	at org.awaitility.core.ConditionFactory.until(ConditionFactory.java:985)
...

Report flaky test

org.keycloak.testsuite.ui.account2.WelcomeScreenTest#applicationsTest

Keycloak CI - Account Console IT (firefox)

org.awaitility.core.ConditionTimeoutException: Assertion condition defined as a org.keycloak.testsuite.util.URLAssert URL expected to begin with: https://localhost:8543/auth/realms/test/protocol/openid-connect/auth ; actual URL: https://localhost:8543/auth/realms/test/account/#/applications within 10 seconds.
	at org.awaitility.core.ConditionAwaiter.await(ConditionAwaiter.java:167)
	at org.awaitility.core.AssertionCondition.await(AssertionCondition.java:119)
	at org.awaitility.core.AssertionCondition.await(AssertionCondition.java:31)
	at org.awaitility.core.ConditionFactory.until(ConditionFactory.java:985)
...

Report flaky test

org.keycloak.testsuite.adapter.example.cors.CorsExampleAdapterTest#angularCorsProductTest

Keycloak CI - Base IT (2)

org.openqa.selenium.TimeoutException: 
Expected condition failed: waiting for text ('iphone') to be present in text ('') in element GrapheneElement -> [[ChromeDriver: chrome on LINUX (07db5e5062b898c89fda3e4c4910dd66)] -> id: output] (tried for 5 second(s) with 500 milliseconds interval)
Build info: version: '3.14.0', revision: 'aacccce0', time: '2018-08-02T20:19:58.91Z'
System info: host: 'fv-az167-188', ip: '10.1.0.41', os.name: 'Linux', os.arch: 'amd64', os.version: '6.2.0-1012-azure', java.version: '17.0.8.1'
Driver info: org.openqa.selenium.chrome.ChromeDriver$$EnhancerByGraphene$$73c7048c
...

Report flaky test

jonkoops
jonkoops previously approved these changes Oct 14, 2023
@jonkoops jonkoops left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes to the front-end look fine to me.

@hmlnarik
Copy link
Copy Markdown
Contributor Author

Rebased to include fix for #23965

@ghost ghost left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unreported flaky test detected, please review

@ghost
ghost commented Oct 16, 2023
Copy link
Copy Markdown

Unreported flaky test detected

If the below flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR.

org.keycloak.testsuite.x509.X509BrowserCRLTest#loginSuccessWithCRLSignedWithIntermediateCA3FromTruststore

Keycloak CI - FIPS IT (non-strict)

java.lang.RuntimeException: Could not create statement
	at org.jboss.arquillian.junit.Arquillian.methodBlock(Arquillian.java:313)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
...

Report flaky test

@hmlnarik hmlnarik changed the title Support for transient sessions for brokered accounts Support for transient users for brokered accounts Oct 18, 2023
@hmlnarik
hmlnarik force-pushed the 11334-transient-sessions branch 2 times, most recently from 5e1352c to 7684e37 Compare October 18, 2023 12:10
@thomasdarimont
Copy link
Copy Markdown
Contributor

Is this supposed to work with the offline_access / OfflineSession feature?

@mposolda
mposolda commented Oct 20, 2023 •
Copy link
Copy Markdown
Contributor

@hmlnarik Thanks for the PR! It looks great!

Few questions:

  1. Do we want feature flag for this capability? IMO it is sufficient to handle new broker option in admin console and in the configuration to be available just when feature enabled. Or is it unnecessary overhead? PREVIEW feature at 1st can be nice.

  2. Is it possible to add test for those scenarios to ensure they work as expected?:
    -- SSO login
    -- Refresh token
    -- Refresh of offline token (to @thomasdarimont question: I think that offline sessions would work fine and user would be "transient" for the whole lifecycle of offline user session if I understand correctly?)
    -- Sending request to UserInfo endpoint (on consumer realm side)
    -- Sending request to introspection endpoint (on consumer realm side)

  3. I am not 100% sure if we want user to be read-write once it finished authentication and is saved in userSession? I understand it needs to be writable when still in authenticationSession (due update-profile page often displayed to user after first IDP login etc). In general, it might be more clear to me to indicate that if such user tries to update himself in account console (or admin tries to update him), that the message like "User is read-only" is displayed instead of silently updating user, which would be lost upon next login. But I don't know what is best regarding usability here? I am curious for more opinions (WDYT @thomasdarimont @pedroigor ?

  4. Documentation

IMO if we have flag (1) and PREVIEW feature, we don't need 2,3,4 in this PR, but can be done in follow-up PRs (if we need 3 at all, depends on the feedback...) as we have more "freedom" to change the behaviour and/or fix possible bugs and provide docs afterwards in the follow-up PRs if needed.

@mposolda
mposolda requested a review from pedroigor October 20, 2023 07:20
@hmlnarik
Copy link
Copy Markdown
Contributor Author

Is this supposed to work with the offline_access / OfflineSession feature?

@thomasdarimont This is a good question. It should (as also @mposolda replied) and I've added a test for it based on OfflineSessionTest into the code base.

@mposolda

1. Do we want feature flag for this capability? IMO it is sufficient to handle new broker option in admin console and in the configuration to be available just when feature enabled. Or is it unnecessary overhead? PREVIEW feature at 1st can be nice.

I've added TRANSIENT_USERS feature flag. Could you please check if this approach works for you?

2. Is it possible to add test for those scenarios to ensure they work as expected?:
-- SSO login
-- Refresh token
-- Refresh of offline token (to @thomasdarimont question: I think that offline sessions would work fine and user would be "transient" for the whole lifecycle of offline user session if I understand correctly?)
-- Sending request to UserInfo endpoint (on consumer realm side)
-- Sending request to introspection endpoint (on consumer realm side)

It is. I have currently only added the offline_access test since that one is most cumbersome, and will work on the others later. Could you please check if they are sufficient?

3. I am not 100% sure if we want user to be read-write once it finished authentication and is saved in userSession? I understand it needs to be writable when still in authenticationSession (due update-profile page often displayed to user after first IDP login etc). In general, it might be more clear to me to indicate that if such user tries to update himself in account console (or admin tries to update him), that the message like "User is read-only" is displayed instead of silently updating user, which would be lost upon next login. But I don't know what is best regarding usability here? I am curious for more opinions (WDYT @thomasdarimont @pedroigor ?

I would not mind either way, but I need to check if this is possible. Not sure especially in case of consents (case where the user authenticates to two applications but additional consent is required for the second one). Also when the user in the user session is writable, it is possible for the admin to tune the roles by assigning the roles to the user from admin console directly instead of doing that via mappers and logout-login trial rounds which may lead to more convenient user (admin) experience.

@mposolda
mposolda commented Oct 20, 2023 •
Copy link
Copy Markdown
Contributor

I've added TRANSIENT_USERS feature flag. Could you please check if this approach works for you?

@hmlnarik Thanks! I would like to approve your PR once tests are OK (Updates are needed in quarkus integration tests always when new feature is added. You can check recent PR from @thomasdarimont on what needs update when new feature is added #23892 )

  1. Is it possible to add test for those scenarios to ensure they work as expected?:
    -- SSO login
    -- Refresh token
    -- Refresh of offline token (to @thomasdarimont question: I think that offline sessions would work fine and user would be "transient" for the whole lifecycle of offline user session if I understand correctly?)
    -- Sending request to UserInfo endpoint (on consumer realm side)
    -- Sending request to introspection endpoint (on consumer realm side)

It is. I have currently only added the offline_access test since that one is most cumbersome, and will work on the others later. Could you please check if they are sufficient?

That works for me, Thanks!

  1. I am not 100% sure if we want user to be read-write once it finished authentication and is saved in userSession? I understand it needs to be writable when still in authenticationSession (due update-profile page often displayed to user after first IDP login etc). In general, it might be more clear to me to indicate that if such user tries to update himself in account console (or admin tries to update him), that the message like "User is read-only" is displayed instead of silently updating user, which would be lost upon next login. But I don't know what is best regarding usability here? I am curious for more opinions (WDYT @thomasdarimont @pedroigor ?

I would not mind either way, but I need to check if this is possible. Not sure especially in case of consents (case where the user authenticates to two applications but additional consent is required for the second one). Also when the user in the user session is writable, it is possible for the admin to tune the roles by assigning the roles to the user from admin console directly instead of doing that via mappers and logout-login trial rounds which may lead to more convenient user (admin) experience.

Good points. Regarding consents, it looks that they may still work even for read-only users? The methods for CRUD consents are not part of the UserModel itself. Which makes me wondering if consents are really stored in-memory for "transient users" in your PR or if they are stored in the DB? It will be good to make sure to not persist them to the DB for transient users IMO (due "dead" records in the DB after transient user is removed from memory etc). However IMO this can be done as a follow-up PR (if it is really an issue) as we have the PREVIEW flag?

@hmlnarik
hmlnarik force-pushed the 11334-transient-sessions branch from 8b063e5 to d46ea90 Compare October 23, 2023 11:08
@ghost
ghost commented Oct 24, 2023
Copy link
Copy Markdown

Unreported flaky test detected

If the below flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR.

org.keycloak.testsuite.x509.X509BrowserCRLTest#loginSuccessWithCRLSignedWithIntermediateCA3FromTruststore

Keycloak CI - FIPS IT (strict)

java.lang.RuntimeException: Could not create statement
	at org.jboss.arquillian.junit.Arquillian.methodBlock(Arquillian.java:313)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
...

Report flaky test

org.keycloak.testsuite.x509.X509BrowserCRLTest#loginFailedWithIntermediateRevocationListFromFile

Keycloak CI - FIPS IT (strict)

java.lang.RuntimeException: Could not create statement
	at org.jboss.arquillian.junit.Arquillian.methodBlock(Arquillian.java:313)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
...

Report flaky test

@ghost ghost left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unreported flaky test detected, please review

@ghost ghost left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unreported flaky test detected, please review

@ghost
ghost commented Oct 24, 2023
Copy link
Copy Markdown

Unreported flaky test detected

If the below flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR.

org.keycloak.testsuite.x509.X509BrowserCRLTest#loginSuccessWithCRLSignedWithIntermediateCA3FromTruststore

Keycloak CI - FIPS IT (strict)

java.lang.RuntimeException: Could not create statement
	at org.jboss.arquillian.junit.Arquillian.methodBlock(Arquillian.java:313)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
...

Report flaky test

org.keycloak.testsuite.x509.X509BrowserCRLTest#loginFailedWithIntermediateRevocationListFromFile

Keycloak CI - FIPS IT (strict)

java.lang.RuntimeException: Could not create statement
	at org.jboss.arquillian.junit.Arquillian.methodBlock(Arquillian.java:313)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
...

Report flaky test

@thomasdarimont
thomasdarimont commented Oct 24, 2023 •
Copy link
Copy Markdown
Contributor

Thanks for your clarifications @hmlnarik!

Two topics come to my mind when I think this feature.

  • How to ensure stable user identifiers?
  • What about increased memory consumption?

Stable user identifier

When the user information is only transient, then it is IMHO cruicial to ensure that the generated user identifier is stable, since it is exposed to applications. Applications might then rely on the user identifier (userUuid) to store service related information. Is it feasible to generate a stable user identifier based on the information provided by the IdP or are we depedent on the IdP to generate a stable identifier for the user?

Memory Consumption

However, I think this will impact memory consumption in usage scenarios with many offline sessions, which might be valid for quite a long time (multiple days, weeks, months...).
Currently, I see customer systems that have 10s of millions of offline sessions and 10s GB of heap consumed just by offline session foot-print in memory (although this idea might help with that a bit).

For reference: currently an offline user session is stored like this in the database - a representation of this is stored in infinispan.
image
The base64 encoded KC_DEVICE_NOTE decodes to a string like this:

'{"ipAddress":"172.26.0.1","os":"Other","osVersion":"Unknown","browser":"Apache-HttpClient/4.5.14","device":"Other","lastAccess":0,"mobile":false}'

IMHO, This encoding is wasteful as the information is rarely used and could be encoded more efficiently. I think there is room for optimization here. :)

From your PR I see that you use the offline session entity to store the "transient" user data as a session note here: https://github.com/keycloak/keycloak/pull/23977/files#diff-1445188baf4e2f8d90de96fc76d0423dd86425d2a0a9ede5c3e20d88c3f85077R762

I was wondering whether this will increase memory consumption even more, e.g. if this is used in large real world CIAM use cases.

For completeness: an offline client session from the database:
image
Also here some optimizations might be possible, e.g. removing "clientId" from the auth notes as it is already stored on the entity , unless this information is needed, e.g. to find and logout all sessions of a given client.
Another one is using shorter keys like loa instead of level-of-authentication.

These changes might look tiny, but optimizations like this can have a noticeable impact on large systems.

@mposolda
Copy link
Copy Markdown
Contributor

@thomasdarimont Good points!

IMO the stable user identifier might be useful for most of the cases. If we can use ID relying on "ID from IDP" instead of using broker session ID (which is not stable as it would differ between logins), we can ensure stable ID IMO?

One advantage can be application compatibility, but also when returning back to consents: Stable ID will allow to use persistent consents as even if the user himself is not saved in the Keycloak DB, the consents can be persisted and user won't need to confirm consent at every login as long as IDP ID is stable and consents are persisted in Keycloak DB. Here I am not 100% sure if consents should be persistent or transient by default. Looks rather transient as I suppose lots of people, who want "Transient users" feature, usually want this due to very rare login of their users? Also not 100% sure if there is foreign key from "consents" table to the USER_ENTITY table (or other tables), which would prevent to save consent for the user, which is not persisted anywhere in Keycloak DB, but I guess this can be solved if needed...

Regarding memory: It looks that we have lots of space for improvements in general. Some notes are not relevant for offline sessions at all. Also the "keys" can be shortened to avoid repeating and/or we can maybe come with more memory friendly format than using JSON for the "data". Maybe we can come with some SPI, which will allow flexibility for what should go to offline sessions? Like "Offline session mapper SPI" or something like that?

IMO both those points can be addressed as follow-ups. Especially "offline session tuning" is separate topic not directly related to this one though as it is already a problem (without this PR being merged). I would just add a note (or warning) to the documentation that using "transient users" can increase memory consumption. And especially for offline sessions, people might be encouraged to consider whether to use this or rather use "persistent" users if they need offline sessions.

@thomasdarimont
thomasdarimont commented Oct 25, 2023 •
Copy link
Copy Markdown
Contributor

Thanks for your thoughts, Marek!

I wonder if it would make sense to store brokered users as dedicated entities still, e.g. when the scope offline_access is used - instead of just storing the user data as a session note (SESSION_NOTE_LIGHTWEIGHT_USER). Having dedicated user entities allows the use of existing structures, store consents, and more straightforward queries. When persisted in the database, the user session notes SESSION_NOTE_LIGHTWEIGHT_USER is another form to make the user persistent - but it's much harder to use (queries, consents, etc.).

Regarding the Offline-Session Optimization:
Offline-Session Optimization should get more priority as most customer systems that had trouble with scalability were mainly caused by too much data stored in the offline session cache. If we could do minimal optimizations here, e.g., removing the unnecessary data, then this would IMHO help improve the system stability and reduce the memory footprint a LOT.

I don't think that users should need an "Offline session mapper SPI," but for internal purposes, it would open up additional optimizations like using different encoding for the data. The SPI should allow to read/write the data blob of an offline session.
But let's move that to a dedicated discussion.

@hmlnarik
hmlnarik commented Oct 25, 2023 •
Copy link
Copy Markdown
Contributor Author

@thomasdarimont Thank you for the detailed concerns.

  • Stable identifiers. The transient users do not intentionally have stable identifiers of their own. The users leave no trace in Keycloak database. However, as long as it is desirable, it can be done similarly to similar handling transient name ID in SAML assertions: A claim or SAML attribute containing a stable identifier from the original identity provider can be mapped to the lightweight user attribute and later mapped from there to individual clients.

  • Memory consumption. I agree with @mposolda that this is much wider problem than the lightweight users in sessions and should be approached as such. The specific problem for offline sessions is there only if the mapper for offline_access exists for lightweight users, enabling offline sessions, which can and should be documented, and explicitly discouraged since it seems to be in conflict with the original intention of transient users. Decision to enable it should be very well justified.
    The querying for the users can be still done similarly to what has been supported up until now, i.e. by broker alias, broker username and / or broker session ID.
    Perhaps could you introduce an issue / discussion on optimizing offline session storage? On a related note I also wonder whether disabling offline session preloading and limiting the number of offline sessions in the cache would not solve this issue, but that's for a separate discussion?

@thomasdarimont
thomasdarimont commented Oct 25, 2023 •
Copy link
Copy Markdown
Contributor

Thanks for your comments @hmlnarik! Perhaps we could use the discussion here: #24263 to collect ideas for optimizing the offline session feature.

I agree that the offline session limitations for transient users must be clearly documented.
Maybe there is a way to save the minimum required information such as consents, own attributes etc. as custom session notes. These are then saved in the database in the case of an offline session.
Provided that the user identifiers are stable, one could then work with the consent information and, if necessary, additional attributes as long as the offline access consent exists. As soon as the offline access consent is removed (and with it the offline session), all the associated consents are gone and must be granted again the next time the user logs in.

@hmlnarik
Copy link
Copy Markdown
Contributor Author

I believe that there are the following use cases, each covering a differrent feature:

  1. Users are stored in Keycloak until explicitly deleted by admin - this is what we have now
  2. Users are stored in Keycloak until expired or explicitly deleted - not implemented yet, see Expiration of users, clients, and sessions #10370
  3. Users are stored in Keycloak until logged out (in memory) - this PR [consents excluded]
  4. Users are stored in Keycloak until logged out with ability create in offline sessions - this PR [consents excluded]

The difference between Options 1,2 and 3,4 is whether the user owns the decision when their data is removed from Keycloak. This PR adds capability to do so, supporting cases 3 and 4. Technically it uses purely the sessions to make it straightforward to implement and assure correct behaviour. For this reason I would prefer storing consents (in a follow-up PR) in the session data only, similarly to the user record. Related to Thomas's comment: it can use custom session notes storage in the future, the rule would be to keep it local to the session.

Stable ID will allow to use persistent consents as even if the user himself is not saved in the Keycloak DB, the consents can be persisted and user won't need to confirm consent at every login as long as IDP ID is stable and consents are persisted in Keycloak DB.

The aim of the transient users feature is to leave any trace of the user or information correlable with a user out of Keycloak database. (This relates e.g. to storage limitation as specified in GDPR Art. 5.) Keycloak as a system cannot determine what the appropriate storage period is however an admin can. If the admin determines that the users should be kept in Keycloak as transient, they may knowingly disable or enable offline access and I believe it is correct to assume that Keycloak does not accidentally store any such information which would lead to personally-identifiable data.

Keeping consents of transient users in the database with a identifiable stable identifier would leave a trace of the user there which can lead to identification of the user: consents relate to (group of) applications which may be used by a specific (group of) users. This seems to me to go against the original intention of keeping no direct or indirect information about the user stored after the user vanishes from the system. Persistent consents would only be possible for persistent users.

I will copy / summarize this discussion into an follow-up issue for consents.

@hmlnarik
Copy link
Copy Markdown
Contributor Author

Thank you everyone for comments and reviews. I've introduced #24277, #24278, and #24279 as follow-up issues, and am merging the PR now.

@hmlnarik
hmlnarik merged commit c036980 into keycloak:main Oct 25, 2023
@mposolda
Copy link
Copy Markdown
Contributor

@hmlnarik +1 for having "transient consents for transient users" as the default user and priority. We can see later based on the feedback if we ever need also "persistent consents for transient users".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add option to not import user from brokers

4 participants

0