Skip to content

Add support for looking up client secrets via Vault SPI#39650

Merged
mposolda merged 7 commits into
keycloak:mainfrom
Nordix:vault-client-secret
Jan 28, 2026
Merged

Add support for looking up client secrets via Vault SPI#39650
mposolda merged 7 commits into
keycloak:mainfrom
Nordix:vault-client-secret

Conversation

@tsaarni

@tsaarni tsaarni commented May 12, 2025

Copy link
Copy Markdown
Contributor

This PR adds support for resolving client secrets via the Vault SPI, enabling retrieval from secure storage instead of storing them in plaintext in the database. While the Vault SPI has some usability limitations, this approach is less invasive than alternatives like hashing or introducing new encrypt/decrypt SPI. It also preserves the plaintext access to the secret, which is required by existing use cases.

Notes:

(1) At the moment, the admin console only supports generating new random client secrets. There is no way for administrators to specify a vault reference value. However, the Admin REST API does support setting the client secret explicitly, including reference values. To fully support this feature, the admin console will need to be extended to allow entering reference values as well. Note: If a new random client secret is generated via the admin console, it will overwrite the existing reference.

(2) The client secret rotation feature introduces additional complexity. Triggering rotation will overwrite any reference-based secret with a newly generated random secret.

(3) Since the client secret may be accessed frequently, using a remote Vault SPI implementation with network communication can introduce latency. To avoid repeated lookups, Vault SPI implementation may choose to add caching.

TODO

  • Add test cases for the new Vault-based client secret resolution.
  • Extend the admin console to support setting client secret references.
  • Add documentation.
  • Update documentation with new screenshot.

Fixes #13102

@tsaarni tsaarni force-pushed the vault-client-secret branch from e70f337 to 2cdc36d Compare May 12, 2025 18:24
@tsaarni

tsaarni commented May 12, 2025

Copy link
Copy Markdown
Contributor Author

Hi @mhajas, I noticed that you had opened an issue requesting this feature a few years ago. I’m interested in contributing it and would be glad to continue working on it. Before proceeding, I wanted to check if this is still a feature you'd consider including in Keycloak?

@mhajas

mhajas commented May 13, 2025

Copy link
Copy Markdown
Contributor

Thank you for the PR @tsaarni. I would say it still makes sense to include this since it is highly upvoted issue. I would be happy to help or find someone else who would be willing to help.

I will have a look at the changes later this week. Let me know if you need something else at this stage.

@mhajas mhajas self-assigned this May 13, 2025
@tsaarni tsaarni force-pushed the vault-client-secret branch 3 times, most recently from f45b82d to 142b5c6 Compare May 26, 2025 07:37
@tsaarni tsaarni force-pushed the vault-client-secret branch from 142b5c6 to 696e7f5 Compare June 11, 2025 12:32
@tsaarni tsaarni marked this pull request as ready for review June 11, 2025 12:32
@tsaarni tsaarni requested review from a team as code owners June 11, 2025 12:32
@tsaarni

tsaarni commented Jun 11, 2025

Copy link
Copy Markdown
Contributor Author

I've now marked the PR as ready for reviews.

I still have a couple of basic question regarding the UI changes.

(1) I didn't add an option to set the client secret during client creation. As a result, the client is always created with randomly generated client secret (as before), even if administrator already knows they want to use a vault reference. In this case, they'll need to first create the client with a random secret, then go to the "Credentials" tab to configure the vault reference.

Do you think this is acceptable from a user experience perspective?

(2) In he "Credentials" tab, there was previously a separator line between authenticator selector and the client secret field:

client-credentials-before

I've removed that separator and moved the Client Secret field above the "Save" button, to make i more consistent with the layout used for the other authenticators:

client-credentials

I haven't updated the documentation with a new UI screenshot yet, as I'd like to agree on the approach first.

(3) Side effect of this change is that administrators can now set any specific client secret directly through the UI, which wasn’t previously possible. This capability has existed via the REST API, but not in the UI until now. Hope this is acceptable.

(4) In other Vault SPI use cases, the secret stored in the vault is not accessible through the UI. However, for OIDC clients, exporting the client configuration from the UI previously included the actual secret. The configuration comes from the client representation, which contains the vault reference instead of the secret itself. I haven’t found a workaround yet, and I’m uncertain whether the secret should be resolved during export. This was just my misunderstanding. There is separate endpoint for downloading client config so I changed that to resolve the vault secret. Now the downloaded client configuration file has the secret itself instead of vault reference.

Lastly, I haven’t used React Hook Form before, but I tried to follow the existing patterns used elsewhere in the code.

@tsaarni tsaarni force-pushed the vault-client-secret branch from 696e7f5 to 9afda53 Compare August 15, 2025 06:22
@tsaarni

tsaarni commented Aug 15, 2025

Copy link
Copy Markdown
Contributor Author

I've updated the PR and addressed the (4) client config download issue I had. I’m looking forward to receive reviews and happy to address any comments to help get this merged.

@tsaarni

tsaarni commented Nov 3, 2025

Copy link
Copy Markdown
Contributor Author

I've rebased the PR.
Since @mhajas is no longer involved with Keycloak, I was wondering if another maintainer could review it. Thank You!

@tsaarni tsaarni force-pushed the vault-client-secret branch 2 times, most recently from aea4824 to 638e422 Compare November 20, 2025 12:06
@tsaarni tsaarni force-pushed the vault-client-secret branch from 638e422 to 06e3fff Compare January 21, 2026 15:42
If vault SPI is defined, lookup client secret (and rotated client secret) from
vault implementation.

Allow editing client secret to set vault reference in the UI.

Signed-off-by: Tero Saarni <tero.saarni@est.tech>
@tsaarni tsaarni force-pushed the vault-client-secret branch 2 times, most recently from 06e3fff to 3f915a4 Compare January 21, 2026 15:43

@rmartinc rmartinc 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.

Thanks @tsaarni for the PR!

I have added some comments. I worked with this branch for the review. So you can take whatever you need from that extra test commit i played with.

I have doubts about the admin console change. Asking @edewit if he can have a better idea.

Probably, when the PR is ready will need a note in the release notes about the change. But we can talk about that later.

Comment on lines +50 to +54
<Controller
name="secret"
control={form.control}
render={({ field }) => <PasswordInput id={id} {...field} />}
/>

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.

With this change we are allowing editing any secret (even for view only users that cannot submit). I think that this was initially read-only because the idea is only allow the generate button. This way the passwords are secure/complex. Maybe we should maintain it read-only initially and allow editing when changed to visible (only for admins that can edit). Not sure about this. I thought about showing a message when toggle to visible, commenting about complexity and so on and so forth... But maybe it's too much.

But I would at least present it read-only and just enable editing when toggled to visible if the admin has permissions for edit (if not it remains read-only).

Change the documentation accordingly if you think my proposal is better.

@edewit @tsaarni WDYT? Any other idea?

@tsaarni tsaarni Jan 23, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think that this was initially read-only because the idea is only allow the generate button. This way the passwords are secure/complex.

Likely true, though the client secrets were possible to change via REST API anyways.

But I would at least present it read-only and just enable editing when toggled to visible if the admin has permissions for edit (if not it remains read-only).

Thanks! I had missed the readOnly={!isManager}. Regarding being editable only after toggling visibility, I suppose <PasswordInput> component remains editable by design even when password is not revealed?

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.

I was thinking about something similar to rmartinc@d59fa46. I mean, by default is read-only, and only when revealed is also editable. But I'm not sure if I'm just overthinking and complicating the UI. We can go even farther and can show a warning or similar before make it editable with this idea.

@edewit @mposolda WDYT? Do we always allow modifying the secret or just when revealing it? I was trying to make the generate button the easiest path except if you really want to type the secret (because you want a vault or any other reason).

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.

I think it's fine to leave it like this that when you want to edit it you can only do that when it's viewable, otherwise we would have to have 2 fields to confirm the new password. One thing to note is instead of using <Control it would be better to use

            <PasswordControl
              name="secret"
              isDisabled={!isManager}
            />

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.

Thanks @edewit! I think this is using directly the PasswordInput instead of the PasswordControl because there are more elements in the row (the copy and regenerate buttons and more things for the client secret rotation). And you need to change the PasswordControl to add everything inside it.

Then @tsaarni, if Erik thinks that it is OK like it is now, just change the the read-only for disabled (it's the same and I think that it works like the rest of fields) and that's all:

            <Controller
              name="secret"
              control={form.control}
              render={({ field }) => (
                <PasswordInput id={id} {...field} isDisabled={!isManager} />
              )}
            />

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks! I've changed from readOnly to isDisabled.

@ahus1 ahus1 removed their assignment Jan 23, 2026
Signed-off-by: Tero Saarni <tero.saarni@est.tech>
Signed-off-by: Tero Saarni <tero.saarni@est.tech>
@tsaarni

tsaarni commented Jan 23, 2026

Copy link
Copy Markdown
Contributor Author

Big thanks for review @rmartinc! I incorporated everything from your commit, thanks for making it available :)

Signed-off-by: Tero Saarni <tero.saarni@est.tech>
@mposolda mposolda self-assigned this Jan 26, 2026

@rmartinc rmartinc 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.

Thanks @tsaarni! LGTM!

@mposolda I think this is OK. The only missing point is that probably we need to add a little release note for 26.6 about this. Don't know if you want to add it in this PR or later.

@mposolda mposolda 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.

@tsaarni @rmartinc @edewit Thanks for the PR and review!

Added few minor comments inline.

Besides that, I am missing a bit a "Revert" button on the page with client credentials. This button was not present here before this PR as well (and is also missing for "X509" credential where it would also makes sense). But with disabled "Regenerate" button after doing some changes in client-secret, I am missing it a bit more now.

For example: Assume I want to edit client-secret by hand, so I would start typing something in the client-secret field. But then I decide I rather prefer to "Regenerate" secret instead of manually updating it. I cannot do that very easily (unless I delete it to previous value OR unless I go back from the client entirely OR unless I am first "Save" it and then "Regenerate" as next step, which all are not so great options from UX perspective IMO).

@tsaarni @rmartinc @edewit Curious for the feedback on this. If you think "Revert" button not needed, I am OK with accepting this without "revert" button (As I can see that "Revert" button can imply that "Regenerating" client secret could be also reverted, but it actually could not as REST request for regenerate was already saved and previous secret already lost. So "revert" button should be rather present/enabled just if Save button is enabled as well).


@Override
public Map<String, Object> getAdapterConfiguration(ClientModel client) {
public Map<String, Object> getAdapterConfiguration(KeycloakSession session, ClientModel client) {

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.

Minor: Should not this method use the same like ClientIdAndSecretAuthenticator.getAdapterConfiguration to actually obtain secret for adapters?

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.

Yep! I missed this one. It should also use the vault to get the secret.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I only planned to add vault support for the client secret authenticator but I now tested the JWT client secret authenticator as well and it's not working with vault references. Not sure what's needed to implement vault support for that as well.

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.

OK, but then this should be at least managed as follow-up. The JWTClientSecretAuthentication is just an authenticator that uses the secret as the HMAC to sign... We would need also tests for this. @mposolda WDYT?

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.

Ah, ok. It would be good to update that one as well for consistency. It seems this authenticator uses key, which is created from client-secret and secret is obtained in ClientMacSignatureVerifierContext (not 100% sure it is this class. You can likely doublecheck).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@mposolda @rmartinc I've now added support for JWT client secret authenticator + test case.

@rmartinc

Copy link
Copy Markdown
Contributor

@mposolda @tsaarni I'm OK adding the revert button.

Signed-off-by: Tero Saarni <tero.saarni@est.tech>
Signed-off-by: Tero Saarni <tero.saarni@est.tech>
Signed-off-by: Tero Saarni <tero.saarni@est.tech>

@mposolda mposolda 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.

@tsaarni @rmartinc @edewit Thanks! Revert button can be possibly nice, but likely not strictly needed. Could be added as a follow-up somewhen if people ask for it ( @tsaarni If you want to contribute it in a follow-up PR, it would be welcome :-) )

@mposolda mposolda merged commit cb4c533 into keycloak:main Jan 28, 2026
81 checks passed
@tsaarni

tsaarni commented Jan 28, 2026

Copy link
Copy Markdown
Contributor Author

I'm working on the button right now, so I will come back with a button bit later today (or admitting defeat and moving it to someone else to follow-up ;) )

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 support for specifying client.secret using vault

6 participants