8000 Add version column to credential table to avoid simultaneous recovery codes updates by rmartinc · Pull Request #38313 · keycloak/keycloak · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add version column to credential table to avoid simultaneous recovery codes updates #38313

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 24, 2025

Conversation

rmartinc
Copy link
Contributor
@rmartinc rmartinc commented Mar 21, 2025

Closes #26106

The update credential now locks using Hibernate's optimistic locking and the model can decide if the update is valid based on the current value. This way the recovery credential model can check if the current value is valid for the update. In the case of the recovery codes, new value should be one code less than the current one, because recovery codes always use the next code and remove it. I have also managed the idea of adding a new method in the credential interfaces. I have been going back and forth between the two solutions. I see pros and cons in both solutions. IMHO changing interfaces is cleaner but it's a backwards incompatible change, and I think there will be implementations of those interfaces out there for other credential types. At least I could not find a change for the interfaces that could have a default implementation or similar.

Test added although it is a mess. The idea is adding a delayed authenticator that just sleeps 1s to allow to test the problem. Besides the button should be clicked using JavaScript because if not selenium waits the new page to come and the issue is not reproduced.

Copy link
Contributor
@ahus1 ahus1 left a comment

Choose a reason for hiding this comment

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

@rmartinc - I had a look at this change as it relates to JPA, and IMHO it isn't in all situations doing what you might expect to to JPA intrinsics.

I was debugging this with a password change as just an example. While I didn't test with recovery code, IMHO the locking should work in all cases.

I started Keycloak with --log-level="INFO,org.hibernate.SQL:debug" to get the SQL statements logged.

When changing the password, JPA already loads the data into its persistence context before it reaches the new pessimistic write code. The following line is logged before I reach the newly changed code.

select c1_0.USER_ID,c1_0.ID,c1_0.CREATED_DATE,c1_0.CREDENTIAL_DATA,c1_0.PRIORITY,c1_0.SALT,c1_0.SECRET_DATA,c1_0.TYPE,c1_0.USER_LABEL from CREDENTIAL c1_0 where c1_0.USER_ID=?
2025-03-21 12:31:02,398 DEBUG [org.hibernate.SQL] (executor-thread-4) select ID from CREDENTIAL where ID=? for update

Once I step over the new em.find() with the locking, it only issues a lock for the row.

select ID from CREDENTIAL where ID=? for update

This shows that the row is locked, but the data had already been loaded. So there is actually a small window between loading the data and locking the data, which is IMHO a problem as it would still allow for lost writes.

If you want to prevent concurrent updates / lost writes with a database that is running in read committed mode, I suggest to add a version column. This would then prevent any concurrent update, and it would fail at the end of the transaction when Hibernate flushes and commits.

See

@Version
@Column(name="VERSION")
private int version;
for an example.

@rmartinc
Copy link
Contributor Author

Ok @ahus1, I'll try to update the DB to include the version column.

… codes updates

Closes keycloak#26106

Signed-off-by: rmartinc <rmartinc@redhat.com>
@rmartinc
Copy link
Contributor Author
rmartinc commented Mar 24, 2025

@ahus1 I have updated to use the @Version, it was indeed even more easier. I have done the table update for 26.2 and maybe we don't reach it. I will update for the next one if the PR is not merge in time. I have also changed the PR title with the new title in the commit. Let's see the tests although they passed OK in my branch.

@rmartinc rmartinc changed the title Update recovery codes credential checking exactly one code was consumed Add version column to credential table to avoid simultaneous recovery codes updates Mar 24, 2025
Copy link
Contributor
@ahus1 ahus1 left a comment

Choose a reason for hiding this comment

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

Thank you for this change, this looks good to me.

@ahus1 ahus1 merged commit 734c4af into keycloak:main Mar 24, 2025
75 checks passed
@ahus1
Copy link
Contributor
ahus1 commented Mar 24, 2025

@rmartinc - thank you for the change. I slightly updated the description of the issue which was still referring to pessimistic locking. Please review it as well and update it as necessary to avoid confusion of future developers that might come across this PR.

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.

Recovery Code Validation Race Possible
2 participants
0