Skip to content

Add keycloak OAuth provider#13033

Open
tazouxme wants to merge 10 commits intoapache:mainfrom
tazouxme:feature/add_keycloak_oauth_provider
Open

Add keycloak OAuth provider#13033
tazouxme wants to merge 10 commits intoapache:mainfrom
tazouxme:feature/add_keycloak_oauth_provider

Conversation

@tazouxme
Copy link
Copy Markdown

Description

Add Keycloak as a new OAuth provider. This PR adds two new fields in the UI and in the DB:

  • Authorization URL
  • Token URL

Types of changes

  • Breaking change (fix or feature that would cause existing functionality to change)
  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • Enhancement (improves an existing feature and functionality)
  • Cleanup (Code refactoring and cleanup, that may add test cases)
  • Build/CI
  • Test (unit or integration test code)

Feature/Enhancement Scale or Bug Severity

Feature/Enhancement Scale

  • Major
  • Minor

Screenshots (if appropriate):

details login new_provider

How Has This Been Tested?

Manual testing using local Keycloak instance with basic Realm

@boring-cyborg
Copy link
Copy Markdown

boring-cyborg bot commented Apr 15, 2026

Congratulations on your first Pull Request and welcome to the Apache CloudStack community! If you have any issues or are unsure about any anything please check our Contribution Guide (https://github.com/apache/cloudstack/blob/main/CONTRIBUTING.md)
Here are some useful points:

@sureshanaparti
Copy link
Copy Markdown
Contributor

@blueorangutan package

@blueorangutan
Copy link
Copy Markdown

@sureshanaparti a [SL] Jenkins job has been kicked to build packages. It will be bundled with no SystemVM templates. I'll keep you posted as I make progress.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 16, 2026

Codecov Report

❌ Patch coverage is 60.13514% with 59 lines in your changes missing coverage. Please review.
✅ Project coverage is 18.02%. Comparing base (9f57a4d) to head (1a543ae).
⚠️ Report is 92 commits behind head on main.

Files with missing lines Patch % Lines
...dstack/oauth2/keycloak/KeycloakOAuth2Provider.java 78.20% 12 Missing and 5 partials ⚠️
...ack/oauth2/api/response/OauthProviderResponse.java 25.00% 12 Missing ⚠️
...k/oauth2/api/command/RegisterOAuthProviderCmd.java 15.38% 10 Missing and 1 partial ⚠️
...ack/oauth2/api/command/UpdateOAuthProviderCmd.java 0.00% 8 Missing ⚠️
...pache/cloudstack/oauth2/OAuth2AuthManagerImpl.java 60.00% 4 Missing and 2 partials ⚠️
...g/apache/cloudstack/oauth2/vo/OauthProviderVO.java 75.00% 3 Missing ⚠️
...tack/oauth2/api/command/ListOAuthProvidersCmd.java 0.00% 1 Missing ⚠️
...cloudstack/oauth2/google/GoogleOAuth2Provider.java 80.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main   #13033      +/-   ##
============================================
+ Coverage     18.01%   18.02%   +0.01%     
- Complexity    16474    16621     +147     
============================================
  Files          5976     6024      +48     
  Lines        537858   542229    +4371     
  Branches      66049    66458     +409     
============================================
+ Hits          96882    97742     +860     
- Misses       430052   433464    +3412     
- Partials      10924    11023      +99     
Flag Coverage Δ
uitests 3.52% <ø> (-0.01%) ⬇️
unittests 19.18% <60.13%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds Keycloak as an additional OAuth2/OIDC provider by extending the OAuth provider model/API/schema and exposing new provider URL fields in the UI, along with a new Keycloak authenticator implementation and tests.

Changes:

  • Add authorizeurl and tokenurl fields end-to-end (DB schema, VO/response objects, API commands, and UI config/i18n).
  • Register a new KeycloakOAuth2Provider and include it in the default provider order.
  • Update GitHub token exchange to use a configurable token URL instead of a hardcoded endpoint.

Reviewed changes

Copilot reviewed 17 out of 18 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
ui/src/views/auth/Login.vue Adds Keycloak social login button and consumes authorizeurl from listOauthProvider.
ui/src/config/section/config.js Extends OAuth provider UI config to edit/display authorizeurl/tokenurl and adds keycloak to provider options.
ui/public/locales/en.json Adds UI labels for Authorize URL and Token URL.
ui/public/assets/keycloak.svg Adds Keycloak icon asset for the login UI.
plugins/user-authenticators/oauth2/src/test/java/org/apache/cloudstack/oauth2/keycloak/KeycloakOAuth2ProviderTest.java Introduces unit tests for the new Keycloak provider.
plugins/user-authenticators/oauth2/src/main/resources/META-INF/cloudstack/oauth2/spring-oauth2-context.xml Registers Keycloak provider bean and adds it to default ordering.
plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/vo/OauthProviderVO.java Adds authorizeUrl and tokenUrl columns/fields to the OAuth provider entity.
plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/keycloak/KeycloakOAuth2Provider.java Implements Keycloak OAuth2/OIDC flow using token endpoint + ID token parsing.
plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/google/GoogleOAuth2Provider.java Minor refactor/cleanup and fixes a format string.
plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/github/GithubOAuth2Provider.java Switches GitHub token endpoint to DB-configured tokenUrl.
plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/api/response/OauthProviderResponse.java Extends API response to include authorizeurl and tokenurl.
plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/api/command/UpdateOAuthProviderCmd.java Adds new parameters and returns them in response.
plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/api/command/RegisterOAuthProviderCmd.java Adds new parameters and validates Keycloak requires the URLs.
plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/api/command/ListOAuthProvidersCmd.java Includes new URL fields in list responses.
plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/OAuth2AuthManagerImpl.java Plumbs new URL fields through register/update persistence.
plugins/user-authenticators/oauth2/pom.xml Adds CXF JOSE dependency used for JWT parsing.
engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql Adds authorize_url and token_url columns to cloud.oauth_provider.
api/src/main/java/org/apache/cloudstack/api/ApiConstants.java Adds API constants for authorizeurl and tokenurl.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +54 to +55
protected String idToken = null;

Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

idToken is stored as an instance field. Since provider beans are typically singletons, this shared mutable state can leak between concurrent login attempts. Please avoid storing per-request tokens on the provider instance; keep them local to the method or in a request/session-scoped context.

Copilot uses AI. Check for mistakes.
Comment on lines +146 to +163
private void validateIdToken(String idTokenStr, OauthProviderVO provider) {
JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(idTokenStr);
JwtClaims claims = jwtConsumer.getJwtToken().getClaims();

if (!claims.getAudiences().contains(provider.getClientId())) {
throw new CloudAuthenticationException("Audience mismatch");
}
}

private String obtainEmail(String idTokenStr, OauthProviderVO provider) {
JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(idTokenStr);
JwtClaims claims = jwtConsumer.getJwtToken().getClaims();

if (!claims.getAudiences().contains(provider.getClientId())) {
throw new CloudAuthenticationException("Audience mismatch");
}

return (String) claims.getClaim("email");
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

The ID token is parsed with JwsJwtCompactConsumer but its signature is never verified (and the tests even use {"alg":"none"} tokens). This allows token forgery. Please perform proper OIDC validation: verify JWS signature against the provider's JWKS, and validate standard claims (exp/iat, iss, aud, nonce) before trusting the email claim.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I agree with this comment. The fact is that the token is never checked in Google / Github. Should we introduce it ?

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.

@tazouxme , it makes sense but some conflicting considerations:

  • How much effort will this be?
  • How likely is an exploit and what is the potential damage?

My gut says this is a security feature we might want and not a direct vulnerability (as people can decide not to use a certain provider). Note that my gut has been known to be wrong.

Comment thread ui/src/views/auth/Login.vue Outdated
@blueorangutan
Copy link
Copy Markdown

Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 17513

@DaanHoogland
Copy link
Copy Markdown
Contributor

thanks for the contribution @tazouxme , looks useful. Please have a look at co-pilot’s comments and the pre-commit and license check failures.

@kiranchavala
Copy link
Copy Markdown
Member

Looks very useful feature. Thanks for Contributing @tazouxme

Will test it out, I had previously tested saml authentication with keycloak

https://kiranchavala.in/blog/cloudstack-integration-with-keycloak/

@tazouxme
Copy link
Copy Markdown
Author

Thanks for your positive feedback. There's only a valid point from Copilot regarding the Token validation. Signature should be validated but currently not done for Google / Github. Should this be introduced ?

@DaanHoogland
Copy link
Copy Markdown
Contributor

Thanks for your positive feedback. There's only a valid point from Copilot regarding the Token validation. Signature should be validated but currently not done for Google / Github. Should this be introduced ?

If you feel like implementing it, your efforts are appreciated. I think it can be a separate 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.

6 participants