From cf78e9269fae67a260a41a4a66b4e2f75001b750 Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Sat, 28 Nov 2020 16:47:09 +0100 Subject: [PATCH] fix: no routes for OAuth 2.0 clients with nosecret close: https://github.com/micronaut-projects/micronaut-security/issues/439 --- .../oauth2/routes/OauthRouteBuilder.java | 14 +++- .../oauth2/ConfigurationFixture.groovy | 19 ++++- .../NoClientSecretNoOauthRoutesSpec.groovy | 73 +++++++++++++++++++ 3 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 security-oauth2/src/test/groovy/io/micronaut/security/oauth2/routes/NoClientSecretNoOauthRoutesSpec.groovy diff --git a/security-oauth2/src/main/java/io/micronaut/security/oauth2/routes/OauthRouteBuilder.java b/security-oauth2/src/main/java/io/micronaut/security/oauth2/routes/OauthRouteBuilder.java index 17a7bad18a..2d9a0091bf 100644 --- a/security-oauth2/src/main/java/io/micronaut/security/oauth2/routes/OauthRouteBuilder.java +++ b/security-oauth2/src/main/java/io/micronaut/security/oauth2/routes/OauthRouteBuilder.java @@ -29,6 +29,7 @@ import io.micronaut.security.authentication.Authentication; import io.micronaut.security.oauth2.client.OauthClient; import io.micronaut.security.oauth2.client.OpenIdClient; +import io.micronaut.security.oauth2.configuration.OauthClientConfiguration; import io.micronaut.security.oauth2.configuration.OauthConfiguration; import io.micronaut.security.oauth2.url.OauthRouteUrlBuilder; import io.micronaut.web.router.DefaultRouteBuilder; @@ -37,6 +38,7 @@ import javax.inject.Singleton; import java.util.List; +import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -77,9 +79,17 @@ class OauthRouteBuilder extends DefaultRouteBuilder { } else { AtomicBoolean endSessionRegistered = new AtomicBoolean(); - controllerList.forEach((controller) -> { + for (OauthController controller : controllerList) { OauthClient client = controller.getClient(); String name = client.getName(); + + Optional oauthClientConfiguration = beanContext.findBean(OauthClientConfiguration.class, Qualifiers.byName(name)); + if (oauthClientConfiguration.isPresent() && oauthClientConfiguration.get().getClientSecret() == null) { + if (LOG.isDebugEnabled()) { + LOG.debug("client {} does not define a client secret. Skipping registration of routes", name); + } + continue; + } boolean isDefaultProvider = oauthConfiguration.getDefaultProvider().filter(provider -> provider.equals(name)).isPresent(); BeanDefinition bd = beanContext.getBeanDefinition(OauthController.class, Qualifiers.byName(name)); @@ -133,7 +143,7 @@ class OauthRouteBuilder extends DefaultRouteBuilder { }); } } - }); + } if (!endSessionRegistered.get() && LOG.isDebugEnabled()) { LOG.debug("Skipped registration of logout route. No openid clients found that support end session"); diff --git a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/ConfigurationFixture.groovy b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/ConfigurationFixture.groovy index 51e2dbd6f1..24ba1d4588 100644 --- a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/ConfigurationFixture.groovy +++ b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/ConfigurationFixture.groovy @@ -28,13 +28,24 @@ trait ConfigurationFixture { } Map getOauth2ClientConfiguration() { - Map m = [ - ("micronaut.security.oauth2.clients.${openIdClientName}.client-id".toString()): 'XXXX', - ("micronaut.security.oauth2.clients.${openIdClientName}.client-secret".toString()): 'YYYY', - ] + Map m = [:] + if (clientId) { + m["micronaut.security.oauth2.clients.${openIdClientName}.client-id".toString()] = clientId + } + if (clientSecret) { + m["micronaut.security.oauth2.clients.${openIdClientName}.client-secret".toString()] = 'YYYY' + } if (issuer != null) { m[("micronaut.security.oauth2.clients.${openIdClientName}.openid.issuer".toString())] = issuer } m } + + String getClientId() { + 'XXXX' + } + + String getClientSecret() { + 'YYYY' + } } diff --git a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/routes/NoClientSecretNoOauthRoutesSpec.groovy b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/routes/NoClientSecretNoOauthRoutesSpec.groovy new file mode 100644 index 0000000000..aba8c0003c --- /dev/null +++ b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/routes/NoClientSecretNoOauthRoutesSpec.groovy @@ -0,0 +1,73 @@ +package io.micronaut.security.oauth2.routes + +import io.micronaut.security.oauth2.OpenIdMockEmbeddedServerSpecification +import io.micronaut.web.router.RouteBuilder +import spock.lang.Narrative +import spock.lang.Shared +import spock.lang.Unroll + +@Narrative(''' +For a config such as: + +micronaut: + security: + oauth2: + clients: + foo: + client-id: 'XXX' + openid: + issuer: 'blababla' + bar: + client-id: 'XXX' + client-secret: 'YYY' + openid: + issuer: 'blababla' + +Micronaut creates routes ('/oauth/callback/{name}','/oauth/login/{name}',) for OAuth 2.0 applications with a client secret. +''') +class NoClientSecretNoOauthRoutesSpec extends OpenIdMockEmbeddedServerSpecification { + + @Shared + Set paths = applicationContext.getBeansOfType(RouteBuilder).collect { it.uriRoutes } + .flatten() + .collect { it.uriMatchTemplate.toPathString() } as Set + + @Override + String getClientSecret() { + null + } + + @Override + Map getConfiguration() { + super.configuration + [ + "micronaut.security.oauth2.clients.bar.client-id": 'XXXX', + "micronaut.security.oauth2.clients.bar.client-secret": 'YYYY', + "micronaut.security.oauth2.clients.bar.openid.issuer": issuer + ] + } + + @Unroll + void "#route is not registered because no client secret is specified for OAuth 2.0 application"(String route) { + expect: + paths.every { it != route } + + where: + route << [ + '/oauth/callback/foo', + '/oauth/login/foo', + '/oauth/logout'] + } + + @Unroll + void "#route is registered because client id and client secret are specified for OAuth 2.0 application"(String route) { + expect: + paths.any { it == route } + + where: + route << [ + '/oauth/callback/bar', + '/oauth/login/bar'] + } + + +}