diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/manager/PasswordGrantAuthenticationManager.java b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/manager/PasswordGrantAuthenticationManager.java index 0b343eec3fa..9623f354700 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/manager/PasswordGrantAuthenticationManager.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/manager/PasswordGrantAuthenticationManager.java @@ -161,6 +161,10 @@ Authentication oidcPasswordGrant(Authentication authentication, final IdentityPr if (clientSecret == null && config.getJwtClientAuthentication() == null && config.getAuthMethod() == null) { throw new ProviderConfigurationException("External OpenID Connect provider configuration is missing relyingPartySecret, jwtClientAuthentication or authMethod."); } + if (tokenUrl == null) { + externalOAuthAuthenticationManager.fetchMetadataAndUpdateDefinition(config); + tokenUrl = Optional.ofNullable(config.getTokenUrl()).orElseThrow(() -> new ProviderConfigurationException("External OpenID Connect metadata is missing after discovery update.")); + } String calcAuthMethod = ClientAuthentication.getCalculatedMethod(config.getAuthMethod(), clientSecret != null, config.getJwtClientAuthentication() != null); String userName = authentication.getPrincipal() instanceof String pStr ? pStr : null; if (userName == null || authentication.getCredentials() == null || !(authentication.getCredentials() instanceof String)) { diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManager.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManager.java index 2b523b52940..3cb3962a573 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManager.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManager.java @@ -808,6 +808,14 @@ public KeyInfoService getKeyInfoService() { return keyInfoService; } + public void fetchMetadataAndUpdateDefinition(OIDCIdentityProviderDefinition definition) { + try { + oidcMetadataFetcher.fetchMetadataAndUpdateDefinition(definition); + } catch (OidcMetadataFetchingException e) { + logger.warn("OidcMetadataFetchingException", e); + } + } + protected static class AuthenticationData { private Map claims; diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/authentication/manager/PasswordGrantAuthenticationManagerTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/authentication/manager/PasswordGrantAuthenticationManagerTest.java index 334fcdb0574..613749e69ae 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/authentication/manager/PasswordGrantAuthenticationManagerTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/authentication/manager/PasswordGrantAuthenticationManagerTest.java @@ -415,6 +415,33 @@ void oidcPasswordGrantProviderNoRelyingPartyCredentials() { } } + @Test + void oidcPasswordGrantProviderFailedInOidcMetadataUpdate() { + IdentityProvider localIdp = mock(IdentityProvider.class); + OIDCIdentityProviderDefinition localIdpConfig = mock(OIDCIdentityProviderDefinition.class); + when(localIdp.getOriginKey()).thenReturn("oidcprovider"); + when(localIdp.getType()).thenReturn(OriginKeys.OIDC10); + when(localIdp.isActive()).thenReturn(true); + when(localIdp.getConfig()).thenReturn(localIdpConfig); + when(localIdpConfig.isPasswordGrantEnabled()).thenReturn(true); + when(localIdpConfig.getRelyingPartyId()).thenReturn("oidcprovider"); + when(localIdpConfig.getRelyingPartySecret()).thenReturn(""); + + when(identityProviderProvisioning.retrieveActive("uaa")).thenReturn(Arrays.asList(uaaProvider, ldapProvider, localIdp)); + when(identityProviderProvisioning.retrieveByOrigin("oidcprovider", "uaa")).thenReturn(localIdp); + UaaLoginHint loginHint = mock(UaaLoginHint.class); + when(loginHint.getOrigin()).thenReturn("oidcprovider"); + Authentication auth = mock(Authentication.class); + when(zoneAwareAuthzAuthenticationManager.extractLoginHint(auth)).thenReturn(loginHint); + + try { + instance.authenticate(auth); + fail(""); + } catch (ProviderConfigurationException e) { + assertThat(e.getMessage()).isEqualTo("External OpenID Connect metadata is missing after discovery update."); + } + } + @Test void oidcPasswordGrantProviderJwtClientCredentials() throws ParseException, JOSEException { // Given @@ -450,10 +477,11 @@ void oidcPasswordGrantProviderJwtClientCredentials() throws ParseException, JOSE } @Test - void oidcPasswordGrantNoUserCredentials() { + void oidcPasswordGrantNoUserCredentials() throws MalformedURLException { UaaLoginHint loginHint = mock(UaaLoginHint.class); when(loginHint.getOrigin()).thenReturn("oidcprovider"); Authentication auth = mock(Authentication.class); + when(idpConfig.getTokenUrl()).thenReturn(null).thenReturn(new URL("http://localhost:8080/uaa/oauth/token")); when(zoneAwareAuthzAuthenticationManager.extractLoginHint(auth)).thenReturn(loginHint); try { diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManagerTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManagerTest.java index aa500ec863f..3aa8ff40742 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManagerTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManagerTest.java @@ -52,6 +52,7 @@ import static org.cloudfoundry.identity.uaa.util.UaaMapUtils.entry; import static org.cloudfoundry.identity.uaa.util.UaaMapUtils.map; import static org.cloudfoundry.identity.uaa.util.UaaStringUtils.DEFAULT_UAA_URL; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -482,4 +483,13 @@ protected > String authManager.getClaimsFromToken(codeToken, idp); assertThat(codeToken.getIdToken()).isEqualTo(idTokenJwt); } + + @Test + void fetchOidcMetadata() throws OidcMetadataFetchingException { + OIDCIdentityProviderDefinition mockedProviderDefinition = mock(OIDCIdentityProviderDefinition.class); + OidcMetadataFetcher mockedOidcMetadataFetcher = mock(OidcMetadataFetcher.class); + authManager = new ExternalOAuthAuthenticationManager(identityProviderProvisioning, new RestTemplate(), new RestTemplate(), tokenEndpointBuilder, new KeyInfoService(uaaIssuerBaseUrl), mockedOidcMetadataFetcher); + doThrow(new OidcMetadataFetchingException("error")).when(mockedOidcMetadataFetcher).fetchMetadataAndUpdateDefinition(mockedProviderDefinition); + assertThatNoException().isThrownBy(() -> authManager.fetchMetadataAndUpdateDefinition(mockedProviderDefinition)); + } }