diff --git a/api-gateway/security/gradle.properties b/api-gateway/security/gradle.properties index b4a38f9..4b0ed85 100644 --- a/api-gateway/security/gradle.properties +++ b/api-gateway/security/gradle.properties @@ -1,4 +1,4 @@ -version=2.0.0-SNAPSHOT -knotx.version=2.0.0 +version=2.1.1-SNAPSHOT +knotx.version=2.1.1-SNAPSHOT knotx.conf=knotx docker.image.name=knotx-example/secure-api-gateway \ No newline at end of file diff --git a/api-gateway/security/knotx/conf/openapi.yaml b/api-gateway/security/knotx/conf/openapi.yaml index 6d4a178..bb0ae2b 100644 --- a/api-gateway/security/knotx/conf/openapi.yaml +++ b/api-gateway/security/knotx/conf/openapi.yaml @@ -53,6 +53,16 @@ paths: description: Hello World API protected with JWT '401': description: Unauthorized access + /api/secure/oauth2: + get: + operationId: hello-world-operation-oauth2 + security: + - helloOAuth2Auth: [] + responses: + '200': + description: Hello World API protected with OAuth2 + '401': + description: Unauthorized access components: securitySchemes: @@ -63,3 +73,12 @@ components: type: http scheme: bearer bearerFormat: JWT + helloOAuth2Auth: + type: oauth2 + flows: + authorizationCode: + authorizationUrl: https://accounts.google.com/o/oauth2/v2/auth + tokenUrl: https://accounts.google.com/o/oauth2/v4/token + scopes: + https://www.googleapis.com/auth/userinfo.profile: read user info + diff --git a/api-gateway/security/knotx/conf/routes/operations.conf b/api-gateway/security/knotx/conf/routes/operations.conf index d224aff..6c26ccf 100644 --- a/api-gateway/security/knotx/conf/routes/operations.conf +++ b/api-gateway/security/knotx/conf/routes/operations.conf @@ -29,9 +29,32 @@ routingOperations = ${routingOperations} [ } ] } + { + operationId = hello-world-operation-oauth2 + handlers = [ + { + name = helloOpenIdHandler + config = { + message = "Hello World From Knot.x with OAuth2!" + } + } + ] + } ] securityHandlers = [ + { + schema = helloOAuth2Auth + factory = helloOAuth2AuthFactory + config = { + clientId = ${googleConfig.clientId} + clientSecret = ${googleConfig.clientSecret} + redirectUrl = "http://localhost:8092/api/secure/callback" + scopes = [ + "https://www.googleapis.com/auth/userinfo.profile" + ] + } + } { schema = helloBasicAuth factory = helloBasicAuthFactory diff --git a/api-gateway/security/modules/hello-module/src/main/java/io/knotx/example/hellohandler/HelloWorldOpenIDHandlerFactory.java b/api-gateway/security/modules/hello-module/src/main/java/io/knotx/example/hellohandler/HelloWorldOpenIDHandlerFactory.java new file mode 100644 index 0000000..de5c61d --- /dev/null +++ b/api-gateway/security/modules/hello-module/src/main/java/io/knotx/example/hellohandler/HelloWorldOpenIDHandlerFactory.java @@ -0,0 +1,25 @@ +package io.knotx.example.hellohandler; + +import io.knotx.server.api.handler.RoutingHandlerFactory; +import io.vertx.core.Handler; +import io.vertx.core.json.JsonObject; +import io.vertx.reactivex.core.Vertx; +import io.vertx.reactivex.ext.auth.oauth2.KeycloakHelper; +import io.vertx.reactivex.ext.web.RoutingContext; + +public class HelloWorldOpenIDHandlerFactory implements RoutingHandlerFactory { + + @Override + public String getName() { + return "helloOpenIdHandler"; + } + + @Override + public Handler create(Vertx vertx, JsonObject config) { + return event -> { + String name = KeycloakHelper.idToken(event.user().principal()) + .getString("name"); + event.response().end("Hello " + name); + }; + } +} \ No newline at end of file diff --git a/api-gateway/security/modules/hello-module/src/main/resources/META-INF/services/io.knotx.server.api.handler.RoutingHandlerFactory b/api-gateway/security/modules/hello-module/src/main/resources/META-INF/services/io.knotx.server.api.handler.RoutingHandlerFactory index bf4c1bf..fd108e4 100644 --- a/api-gateway/security/modules/hello-module/src/main/resources/META-INF/services/io.knotx.server.api.handler.RoutingHandlerFactory +++ b/api-gateway/security/modules/hello-module/src/main/resources/META-INF/services/io.knotx.server.api.handler.RoutingHandlerFactory @@ -1 +1,2 @@ -io.knotx.example.hellohandler.HelloWorldHandlerFactory \ No newline at end of file +io.knotx.example.hellohandler.HelloWorldHandlerFactory +io.knotx.example.hellohandler.HelloWorldOpenIDHandlerFactory \ No newline at end of file diff --git a/api-gateway/security/modules/security-module/src/main/java/io/knotx/examples/security/auth/OAuth2HelloHandlerFactory.java b/api-gateway/security/modules/security-module/src/main/java/io/knotx/examples/security/auth/OAuth2HelloHandlerFactory.java new file mode 100644 index 0000000..5ea22c2 --- /dev/null +++ b/api-gateway/security/modules/security-module/src/main/java/io/knotx/examples/security/auth/OAuth2HelloHandlerFactory.java @@ -0,0 +1,48 @@ +package io.knotx.examples.security.auth; + +import io.knotx.server.api.security.AuthHandlerFactory; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.auth.oauth2.OAuth2ClientOptions; +import io.vertx.ext.auth.oauth2.OAuth2FlowType; +import io.vertx.reactivex.core.Vertx; +import io.vertx.reactivex.ext.auth.oauth2.OAuth2Auth; +import io.vertx.reactivex.ext.web.Router; +import io.vertx.reactivex.ext.web.handler.AuthHandler; +import io.vertx.reactivex.ext.web.handler.OAuth2AuthHandler; + +public class OAuth2HelloHandlerFactory implements AuthHandlerFactory { + + private OAuth2AuthHandler oAuth2AuthHandler; + + @Override + public String getName() { + return "helloOAuth2AuthFactory"; + } + + @Override + public AuthHandler create(Vertx vertx, JsonObject config) { + OAuth2ClientOptions clientOptions = new OAuth2ClientOptions() + .setClientID(config.getString("clientId")) + .setClientSecret(config.getString("clientSecret")) + .setSite("https://accounts.google.com") + .setTokenPath("https://www.googleapis.com/oauth2/v4/token") + .setAuthorizationPath("/o/oauth2/auth") + .setUserInfoPath("https://www.googleapis.com/oauth2/v3/userinfo") + .setIntrospectionPath("https://www.googleapis.com/oauth2/v3/tokeninfo") + .setUserInfoParameters(new JsonObject().put("alt", "json")) + .setScopeSeparator(" ") + .setUseBasicAuthorizationHeader(false); + + OAuth2Auth authProvider = OAuth2Auth.create(vertx, OAuth2FlowType.AUTH_CODE, clientOptions); + + oAuth2AuthHandler = OAuth2AuthHandler.create(authProvider, config.getString("redirectUrl")); + config.getJsonArray("scopes").forEach(scope -> oAuth2AuthHandler.addAuthority((String) scope)); + + return oAuth2AuthHandler; + } + + @Override + public void registerCustomRoute(Router router) { + oAuth2AuthHandler.setupCallback(router.route()); + } +} diff --git a/api-gateway/security/modules/security-module/src/main/resources/META-INF/services/io.knotx.server.api.security.AuthHandlerFactory b/api-gateway/security/modules/security-module/src/main/resources/META-INF/services/io.knotx.server.api.security.AuthHandlerFactory index c4bb504..5fba90b 100644 --- a/api-gateway/security/modules/security-module/src/main/resources/META-INF/services/io.knotx.server.api.security.AuthHandlerFactory +++ b/api-gateway/security/modules/security-module/src/main/resources/META-INF/services/io.knotx.server.api.security.AuthHandlerFactory @@ -1,2 +1,3 @@ io.knotx.examples.security.auth.BasicAuthHandlerFactory io.knotx.examples.security.auth.JwtAuthHandlerFactory +io.knotx.examples.security.auth.OAuth2HelloHandlerFactory