Skip to content

Commit 57c9fe0

Browse files
committed
oauth2: resolve the OIDC authorize endpoint server side, fix the login page
- discover and store the authorization endpoint at registration so the UI redirects without reading the discovery document from the browser, which removed the IdP CORS requirement; the UI falls back to discovery when the endpoint is not stored - build the authorize URL with the URL API so an endpoint that already has a query component is preserved - restore the global generic provider list when the domain field is cleared so stale domain-specific buttons are not shown
1 parent 1ff78f7 commit 57c9fe0

4 files changed

Lines changed: 40 additions & 3 deletions

File tree

‎api/src/main/java/org/apache/cloudstack/auth/UserOAuth2Authenticator.java‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,16 @@ default String verifySecretCodeAndFetchEmail(String secretCode, Long domainId, S
8484
return verifySecretCodeAndFetchEmail(secretCode, domainId, providerName);
8585
}
8686

87+
/**
88+
* Resolves the authorization endpoint for the registration whose issuer URL is given, so the UI can
89+
* redirect without reading the discovery document from the browser. Implementations that do not use
90+
* discovery return null.
91+
* @return the authorization endpoint, or null
92+
*/
93+
default String discoverAuthorizeEndpoint(String issuerUrl) {
94+
return null;
95+
}
96+
8797
/**
8898
* Fetches email using the accessToken
8999
* @return returns email

‎plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/OAuth2AuthManagerImpl.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,13 @@ public OauthProviderVO registerOauthProvider(RegisterOAuthProviderCmd cmd) {
214214
}
215215
}
216216

217+
if (StringUtils.isNotBlank(type) && StringUtils.isBlank(authorizeUrl) && StringUtils.isNotBlank(issuerUrl)) {
218+
UserOAuth2Authenticator authenticator = userOAuth2AuthenticationProvidersMap.get(type.toLowerCase());
219+
if (authenticator != null) {
220+
authorizeUrl = authenticator.discoverAuthorizeEndpoint(issuerUrl);
221+
}
222+
}
223+
217224
return saveOauthProvider(provider, description, clientId, secretKey, redirectUri, authorizeUrl, tokenUrl, domainId, type, issuerUrl);
218225
}
219226

‎plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/oidc/GenericOIDCOAuth2Provider.java‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,14 @@ protected OauthProviderVO findRegistration(String providerName, Long domainId) {
208208
return provider;
209209
}
210210

211+
@Override
212+
public String discoverAuthorizeEndpoint(String issuerUrl) {
213+
if (StringUtils.isBlank(issuerUrl)) {
214+
return null;
215+
}
216+
return metadataCache.get(StringUtils.trim(issuerUrl), this::discover).getAuthorizationEndpoint();
217+
}
218+
211219
protected OIDCMetadata getMetadata(OauthProviderVO provider) {
212220
String issuerUrl = StringUtils.trimToNull(provider.getIssuerUrl());
213221
if (issuerUrl == null) {

‎ui/src/views/auth/Login.vue‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ export default {
305305
oauthKeycloakRedirectUri: '',
306306
oauthKeycloakAuthorizeUrl: '',
307307
oauthGenericProviders: [],
308+
globalGenericProviders: [],
308309
oauthLoading: false,
309310
oauthDomainQueried: false,
310311
loginType: 0,
@@ -406,6 +407,7 @@ export default {
406407
const oauthproviders = response.listoauthproviderresponse.oauthprovider || []
407408
this.oauthGenericProviders = oauthproviders.filter(item => item.type === 'oidc' && (item.enabled === true || item.enabled === 'true'))
408409
if (!domain) {
410+
this.globalGenericProviders = this.oauthGenericProviders
409411
oauthproviders.forEach(item => {
410412
if (item.provider === 'google') {
411413
this.googleprovider = item.enabled
@@ -498,6 +500,7 @@ export default {
498500
this.fetchOauthProviders(domain)
499501
} else {
500502
this.oauthDomainQueried = false
503+
this.oauthGenericProviders = this.globalGenericProviders
501504
this.oauthGithubProvider = this.githubprovider
502505
this.oauthGoogleProvider = this.googleprovider
503506
this.oauthKeycloakProvider = this.keycloakprovider
@@ -530,8 +533,7 @@ export default {
530533
loginWithGenericOidc (provider) {
531534
this.handleDomain()
532535
this.$store.commit('SET_OAUTH_PROVIDER_USED_TO_LOGIN', provider.provider)
533-
const discoveryUrl = provider.issuerurl.replace(/\/$/, '') + '/.well-known/openid-configuration'
534-
return fetch(discoveryUrl).then(response => response.json()).then(config => {
536+
const redirectToProvider = (authorizationEndpoint) => {
535537
const state = this.randomToken()
536538
const nonce = this.randomToken()
537539
try {
@@ -546,7 +548,17 @@ export default {
546548
state: state,
547549
nonce: nonce
548550
}
549-
window.location.href = `${config.authorization_endpoint}?${new URLSearchParams(options).toString()}`
551+
const authorizeUrl = new URL(authorizationEndpoint)
552+
Object.entries(options).forEach(([key, value]) => authorizeUrl.searchParams.set(key, value))
553+
window.location.href = authorizeUrl.toString()
554+
}
555+
if (provider.authorizeurl) {
556+
redirectToProvider(provider.authorizeurl)
557+
return
558+
}
559+
const discoveryUrl = provider.issuerurl.replace(/\/$/, '') + '/.well-known/openid-configuration'
560+
return fetch(discoveryUrl).then(response => response.json()).then(config => {
561+
redirectToProvider(config.authorization_endpoint)
550562
}).catch(() => {
551563
this.$notification.error({
552564
message: this.$t('label.error'),

0 commit comments

Comments
 (0)