From 0cd9860964ca1d33e76dc33e7addf0a5f4cdf3b1 Mon Sep 17 00:00:00 2001 From: Daniel Camargo Date: Wed, 28 Sep 2022 13:19:05 -0600 Subject: [PATCH 1/6] working version --- .gitignore | 2 +- README.md | 7 +- pom.xml | 26 ++-- sam/env.json | 5 - sam/env.json.example | 4 +- sam/{event.json => events/authorize.json} | 2 +- sam/events/callback.json | 4 + sam/sam.native.yaml | 4 +- src/main/kotlin/com/acme/AuthCredential.kt | 6 + src/main/kotlin/com/acme/AuthUser.kt | 10 ++ src/main/kotlin/com/acme/GoogleOAuthClient.kt | 128 ++++++++++++++++++ .../kotlin/com/acme/GoogleOAuthProperties.kt | 17 +++ src/main/kotlin/com/acme/GreetingResource.kt | 63 --------- src/main/kotlin/com/acme/ReflectionConfig.kt | 20 +++ .../com/acme/resources/AuthorizeResource.kt | 23 ++++ .../com/acme/resources/CallbackResource.kt | 31 +++++ src/main/resources/application.properties | 5 + src/main/resources/reflection-config.json | 10 +- .../resources/reflection-config.json.example | 58 ++++++++ .../kotlin/com/acme/GreetingResourceIT.kt | 6 - .../kotlin/com/acme/GreetingResourceTest.kt | 19 --- 21 files changed, 330 insertions(+), 120 deletions(-) delete mode 100644 sam/env.json rename sam/{event.json => events/authorize.json} (51%) create mode 100644 sam/events/callback.json create mode 100644 src/main/kotlin/com/acme/AuthCredential.kt create mode 100644 src/main/kotlin/com/acme/AuthUser.kt create mode 100644 src/main/kotlin/com/acme/GoogleOAuthClient.kt create mode 100644 src/main/kotlin/com/acme/GoogleOAuthProperties.kt delete mode 100644 src/main/kotlin/com/acme/GreetingResource.kt create mode 100644 src/main/kotlin/com/acme/ReflectionConfig.kt create mode 100644 src/main/kotlin/com/acme/resources/AuthorizeResource.kt create mode 100644 src/main/kotlin/com/acme/resources/CallbackResource.kt create mode 100644 src/main/resources/reflection-config.json.example delete mode 100644 src/test/kotlin/com/acme/GreetingResourceIT.kt delete mode 100644 src/test/kotlin/com/acme/GreetingResourceTest.kt diff --git a/.gitignore b/.gitignore index efea3d2..06de294 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,4 @@ nb-configuration.xml # Local environment .env -.env.json +env.json diff --git a/README.md b/README.md index 3578c88..e7f8dbb 100644 --- a/README.md +++ b/README.md @@ -18,5 +18,10 @@ Running AWS SAM locally ```bash # Make sure you have your "sam/env.json" file in place -sam local invoke --template sam/sam.native.yaml --event sam/event.json --env-vars sam/env.json +sam local invoke --template sam/sam.native.yaml --event sam/events/authorize.json +``` + +```bash +# Make sure you have your "sam/env.json" file in place +sam local invoke --template sam/sam.native.yaml --event sam/events/callback.json ``` diff --git a/pom.xml b/pom.xml index 1310256..0fde0f8 100644 --- a/pom.xml +++ b/pom.xml @@ -27,13 +27,6 @@ pom import - - io.quarkiverse.googlecloudservices - quarkus-google-cloud-services-bom - ${quarkus.google-cloud-services.version} - pom - import - @@ -53,6 +46,10 @@ io.quarkus quarkus-arc + + io.quarkus + quarkus-grpc + io.quarkus quarkus-resteasy-reactive @@ -83,20 +80,19 @@ - - io.quarkiverse.googlecloudservices - quarkus-google-cloud-secret-manager - com.google.apis google-api-services-oauth2 ${google-api-services-oauth2.version} + + com.google.http-client + google-http-client + src/main/kotlin - src/test/kotlin ${quarkus.platform.group-id} @@ -200,9 +196,11 @@ + + @@ -221,8 +219,10 @@ native true quay.io/quarkus/ubi-quarkus-mandrel:22.2-java11 - 2g + 8g + -H:+RemoveSaturatedTypeFlows,\ + -H:+PrintAnalysisStatistics,\ -H:ResourceConfigurationFiles=resources-config.json,\ -H:ReflectionConfigurationFiles=reflection-config.json,\ -H:EnableURLProtocols=http\,https,\ diff --git a/sam/env.json b/sam/env.json deleted file mode 100644 index 42efa24..0000000 --- a/sam/env.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "function": { - "GOOGLE_CLIENT_SECRET": "secret" - } -} diff --git a/sam/env.json.example b/sam/env.json.example index 42efa24..1b49a84 100644 --- a/sam/env.json.example +++ b/sam/env.json.example @@ -1,5 +1,7 @@ { "function": { - "GOOGLE_CLIENT_SECRET": "secret" + "GOOGLE_CLIENT_ID": "", + "GOOGLE_CLIENT_SECRET": "", + "GOOGLE_CLIENT_REDIRECT_URI": "http://localhost:8080/callback" } } diff --git a/sam/event.json b/sam/events/authorize.json similarity index 51% rename from sam/event.json rename to sam/events/authorize.json index 89c26c0..6c97aff 100644 --- a/sam/event.json +++ b/sam/events/authorize.json @@ -1,4 +1,4 @@ { - "path": "/", + "path": "/authorize", "httpMethod": "GET" } diff --git a/sam/events/callback.json b/sam/events/callback.json new file mode 100644 index 0000000..e5ab5e2 --- /dev/null +++ b/sam/events/callback.json @@ -0,0 +1,4 @@ +{ + "path": "/callback?...", + "httpMethod": "GET" +} diff --git a/sam/sam.native.yaml b/sam/sam.native.yaml index 6975ccb..8473b6a 100644 --- a/sam/sam.native.yaml +++ b/sam/sam.native.yaml @@ -20,7 +20,9 @@ Resources: Environment: Variables: DISABLE_SIGNAL_HANDLERS: true - GOOGLE_CLIENT_SECRET: secret + GOOGLE_CLIENT_ID: null + GOOGLE_CLIENT_SECRET: null + GOOGLE_CLIENT_REDIRECT_URI: null Events: GetResource: Type: Api diff --git a/src/main/kotlin/com/acme/AuthCredential.kt b/src/main/kotlin/com/acme/AuthCredential.kt new file mode 100644 index 0000000..aab5115 --- /dev/null +++ b/src/main/kotlin/com/acme/AuthCredential.kt @@ -0,0 +1,6 @@ +package com.acme + +import com.google.api.client.auth.oauth2.BearerToken +import com.google.api.client.auth.oauth2.Credential + +class AuthCredential : Credential(BearerToken.authorizationHeaderAccessMethod()) diff --git a/src/main/kotlin/com/acme/AuthUser.kt b/src/main/kotlin/com/acme/AuthUser.kt new file mode 100644 index 0000000..246eef9 --- /dev/null +++ b/src/main/kotlin/com/acme/AuthUser.kt @@ -0,0 +1,10 @@ +package com.acme + +import com.google.api.services.oauth2.model.Userinfo +import io.quarkus.runtime.annotations.RegisterForReflection + +@RegisterForReflection +data class AuthUser( + val refreshToken: String, + val userInfo: Userinfo +) diff --git a/src/main/kotlin/com/acme/GoogleOAuthClient.kt b/src/main/kotlin/com/acme/GoogleOAuthClient.kt new file mode 100644 index 0000000..97ef778 --- /dev/null +++ b/src/main/kotlin/com/acme/GoogleOAuthClient.kt @@ -0,0 +1,128 @@ +package com.acme + +import com.google.api.client.auth.oauth2.TokenResponse +import com.google.api.client.googleapis.auth.oauth2.* +import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport +import com.google.api.client.http.apache.v2.ApacheHttpTransport +import com.google.api.client.json.JsonFactory +import com.google.api.client.json.gson.GsonFactory +import com.google.api.services.oauth2.Oauth2 +import com.google.api.services.oauth2.Oauth2Scopes +import java.net.URL +import javax.enterprise.context.ApplicationScoped +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +@ApplicationScoped +class GoogleOAuthClient( + private val properties: GoogleOAuthProperties +) { + + private val logger: Logger = LoggerFactory.getLogger(this.javaClass) + + private val scopes = setOf( + // For basic user information + Oauth2Scopes.OPENID, + Oauth2Scopes.USERINFO_PROFILE, + Oauth2Scopes.USERINFO_EMAIL + ) + + private fun googleAuthorizationCodeFlow(): GoogleAuthorizationCodeFlow { + val jsonFactory: JsonFactory = GsonFactory.getDefaultInstance() + val details = GoogleClientSecrets.Details() + + details.clientId = properties.clientId() + details.clientSecret = properties.clientSecret() + + val clientSecrets = GoogleClientSecrets() + clientSecrets.installed = details + + // Create builder + val builder = GoogleAuthorizationCodeFlow + .Builder(ApacheHttpTransport(), jsonFactory, clientSecrets, scopes) + + return builder + .setAccessType("offline") + .setApprovalPrompt("force") + .build() + } + + fun createAuthUrl(state: String): URL { + logger.debug("Creating a new authentication URL") + val flow = googleAuthorizationCodeFlow() + + val codeRequestUrl: GoogleAuthorizationCodeRequestUrl = flow + .newAuthorizationUrl() + .setRedirectUri(properties.redirectUri()) + .setState(state) + + logger.info("Entries {}", codeRequestUrl.entries.size) + logger.info("Empty in native builds: {}", codeRequestUrl.buildRelativeUrl()) + + return codeRequestUrl.toURL() + } + + fun authenticate(authorizationCode: String): AuthUser { + logger.debug("Fetching a new refresh token") + val flow = googleAuthorizationCodeFlow() + + val codeTokenRequest: GoogleAuthorizationCodeTokenRequest = flow + .newTokenRequest(authorizationCode) + .setRedirectUri(properties.redirectUri()) + + logger.info("Entries {}", codeTokenRequest.entries.size) + val token = codeTokenRequest.execute() + + // Let's save the refresh token in DynamoDB so that every instance will + // be able to reuse it + val httpTransport = ApacheHttpTransport() + val jsonFactory: JsonFactory = GsonFactory.getDefaultInstance() + val credential = AuthCredential() + credential.accessToken = token.accessToken + + val oauth2 = Oauth2 + .Builder(httpTransport, jsonFactory, credential) + .setApplicationName(properties.applicationName()) + .build() + + val userInfo = oauth2 + .userinfo() + .get() + .execute() + + return AuthUser( + refreshToken = token.refreshToken, + userInfo = userInfo + ) + } + + // A new cache will be created for each instance. In other words, each instance will + // resolve a new access token every 59 minutes and 55 seconds. + fun getAuthCredential(refreshToken: String): AuthCredential { + logger.debug("Fetching a new access token") + val httpTransport = GoogleNetHttpTransport.newTrustedTransport() + + val jsonFactory: JsonFactory = GsonFactory.getDefaultInstance() + + val tokenResponse: TokenResponse = GoogleRefreshTokenRequest( + httpTransport, + jsonFactory, + refreshToken, + properties.clientId(), + properties.clientSecret() + ) + .setScopes(scopes) + .setGrantType("refresh_token") + .execute() + + // Instantiate a credential + val credential = AuthCredential() + credential.accessToken = tokenResponse.accessToken + + return credential + } + + fun applicationName(): String { + return properties.applicationName() + } +} diff --git a/src/main/kotlin/com/acme/GoogleOAuthProperties.kt b/src/main/kotlin/com/acme/GoogleOAuthProperties.kt new file mode 100644 index 0000000..c7acb32 --- /dev/null +++ b/src/main/kotlin/com/acme/GoogleOAuthProperties.kt @@ -0,0 +1,17 @@ +package com.acme + +import io.quarkus.runtime.annotations.RegisterForReflection +import io.smallrye.config.ConfigMapping + +@RegisterForReflection +@ConfigMapping(prefix = GoogleOAuthProperties.PREFIX) +interface GoogleOAuthProperties { + fun applicationName(): String + fun clientId(): String + fun clientSecret(): String + fun redirectUri(): String + + companion object { + const val PREFIX = "google" + } +} diff --git a/src/main/kotlin/com/acme/GreetingResource.kt b/src/main/kotlin/com/acme/GreetingResource.kt deleted file mode 100644 index 2ed61d7..0000000 --- a/src/main/kotlin/com/acme/GreetingResource.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.acme - -import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow -import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets -import com.google.api.client.http.apache.v2.ApacheHttpTransport -import com.google.api.client.json.JsonFactory -import com.google.api.client.json.gson.GsonFactory -import com.google.api.services.oauth2.Oauth2Scopes -import javax.ws.rs.GET -import javax.ws.rs.Path -import javax.ws.rs.Produces -import javax.ws.rs.core.MediaType -import org.slf4j.Logger -import org.slf4j.LoggerFactory - - -@Path("/") -class GreetingResource { - - private val logger: Logger = LoggerFactory.getLogger(this.javaClass) - - private val scopes = setOf( - // For basic user information - Oauth2Scopes.OPENID, - Oauth2Scopes.USERINFO_PROFILE, - Oauth2Scopes.USERINFO_EMAIL - ) - - @GET - @Produces(MediaType.TEXT_PLAIN) - fun get(): String { - logger.info("Build auth url") - val flow = googleAuthorizationCodeFlow() - - val codeRequestUrl = flow - .newAuthorizationUrl() - .setRedirectUri("http://localhost:3000") - .setState("some-state") - - logger.debug("Why is this empty in native? {}", codeRequestUrl.entries.size) - - return codeRequestUrl.toURL().toString() - } - - private fun googleAuthorizationCodeFlow(): GoogleAuthorizationCodeFlow { - val jsonFactory: JsonFactory = GsonFactory.getDefaultInstance() - val details = GoogleClientSecrets.Details() - - details.clientId = "some-id" - details.clientSecret = "some-secret" - - val clientSecrets = GoogleClientSecrets() - clientSecrets.installed = details - - val builder = GoogleAuthorizationCodeFlow - .Builder(ApacheHttpTransport(), jsonFactory, clientSecrets, scopes) - - return builder - .setAccessType("offline") - .setApprovalPrompt("force") - .build() - } -} diff --git a/src/main/kotlin/com/acme/ReflectionConfig.kt b/src/main/kotlin/com/acme/ReflectionConfig.kt new file mode 100644 index 0000000..672532d --- /dev/null +++ b/src/main/kotlin/com/acme/ReflectionConfig.kt @@ -0,0 +1,20 @@ +package com.acme + +import com.google.api.client.auth.oauth2.* +import io.quarkus.runtime.annotations.RegisterForReflection + +@Suppress("unused") +@RegisterForReflection( + targets = [ + AuthorizationCodeRequestUrl::class, + AuthorizationCodeResponseUrl::class, + AuthorizationCodeTokenRequest::class, + AuthorizationRequestUrl::class, + PasswordTokenRequest::class, + RefreshTokenRequest::class, + TokenErrorResponse::class, + TokenRequest::class, + TokenResponse::class + ] +) +class ReflectionConfig diff --git a/src/main/kotlin/com/acme/resources/AuthorizeResource.kt b/src/main/kotlin/com/acme/resources/AuthorizeResource.kt new file mode 100644 index 0000000..cbccf5d --- /dev/null +++ b/src/main/kotlin/com/acme/resources/AuthorizeResource.kt @@ -0,0 +1,23 @@ +package com.acme.resources + +import com.acme.GoogleOAuthClient +import javax.ws.rs.GET +import javax.ws.rs.Path +import javax.ws.rs.Produces +import javax.ws.rs.core.MediaType +import org.slf4j.Logger +import org.slf4j.LoggerFactory + + +@Path("/authorize") +class AuthorizeResource(private val googleOAuthClient: GoogleOAuthClient) { + + private val logger: Logger = LoggerFactory.getLogger(this.javaClass) + + @GET + @Produces(MediaType.TEXT_PLAIN) + fun handle(): String { + logger.info("Build auth url") + return googleOAuthClient.createAuthUrl("some-state").toString() + } +} diff --git a/src/main/kotlin/com/acme/resources/CallbackResource.kt b/src/main/kotlin/com/acme/resources/CallbackResource.kt new file mode 100644 index 0000000..0e7611c --- /dev/null +++ b/src/main/kotlin/com/acme/resources/CallbackResource.kt @@ -0,0 +1,31 @@ +package com.acme.resources + +import com.acme.GoogleOAuthClient +import javax.ws.rs.GET +import javax.ws.rs.Path +import javax.ws.rs.Produces +import javax.ws.rs.QueryParam +import javax.ws.rs.core.MediaType +import org.slf4j.Logger +import org.slf4j.LoggerFactory + + +@Path("/callback") +class CallbackResource( + private val googleOAuthClient: GoogleOAuthClient +) { + + private val logger: Logger = LoggerFactory.getLogger(this.javaClass) + + @GET + @Produces(MediaType.TEXT_PLAIN) + fun handle( + @QueryParam("code") code: String, + @QueryParam("state") state: String + ): String { + logger.debug("Get refresh token") + val (refreshToken) = googleOAuthClient.authenticate(code) + + return refreshToken + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e69de29..331947e 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -0,0 +1,5 @@ +google.application-name="Some app" +google.client-id=${GOOGLE_CLIENT_ID} +google.client-secret=${GOOGLE_CLIENT_SECRET} +google.redirect-uri=${GOOGLE_CLIENT_REDIRECT_URI} +quarkus.log.level=INFO diff --git a/src/main/resources/reflection-config.json b/src/main/resources/reflection-config.json index ed64df0..fe51488 100644 --- a/src/main/resources/reflection-config.json +++ b/src/main/resources/reflection-config.json @@ -1,9 +1 @@ -[ - { - "name": "com.google.api.client.http.GenericUrl", - "allPublicMethods": true, - "allDeclaredFields": true, - "allDeclaredMethods": true, - "allDeclaredConstructors": true - } -] +[] diff --git a/src/main/resources/reflection-config.json.example b/src/main/resources/reflection-config.json.example new file mode 100644 index 0000000..8ac2f58 --- /dev/null +++ b/src/main/resources/reflection-config.json.example @@ -0,0 +1,58 @@ +[ + { + "name": "com.google.api.client.http.GenericUrl", + "allPublicMethods": true, + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.google.api.client.auth.oauth2.TokenErrorResponse", + "allPublicMethods": true, + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest", + "allPublicMethods": true, + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.google.api.client.auth.oauth2.AuthorizationCodeTokenRequest", + "allPublicMethods": true, + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": ",com.google.api.client.auth.oauth2.TokenRequest", + "allPublicMethods": true, + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.google.api.client.util.GenericData", + "allPublicMethods": true, + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.google.api.client.util.GenericData.EntrySet", + "allPublicMethods": true, + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "java.util.AbstractMap", + "allPublicMethods": true, + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + } +] diff --git a/src/test/kotlin/com/acme/GreetingResourceIT.kt b/src/test/kotlin/com/acme/GreetingResourceIT.kt deleted file mode 100644 index e393621..0000000 --- a/src/test/kotlin/com/acme/GreetingResourceIT.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.acme - -import io.quarkus.test.junit.QuarkusIntegrationTest - -@QuarkusIntegrationTest -class GreetingResourceIT : GreetingResourceTest() diff --git a/src/test/kotlin/com/acme/GreetingResourceTest.kt b/src/test/kotlin/com/acme/GreetingResourceTest.kt deleted file mode 100644 index 68bd32b..0000000 --- a/src/test/kotlin/com/acme/GreetingResourceTest.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.acme - -import io.quarkus.test.junit.QuarkusTest -import io.restassured.RestAssured.given -import org.hamcrest.CoreMatchers.`is` -import org.junit.jupiter.api.Test - -@QuarkusTest -class GreetingResourceTest { - - @Test - fun testHelloEndpoint() { - given() - .`when`().get("/") - .then() - .statusCode(200) - .body(`is`("https://accounts.google.com/o/oauth2/auth?access_type=offline&approval_prompt=force&client_id=some-id&redirect_uri=http://localhost:3000&response_type=code&scope=openid%20https://www.googleapis.com/auth/userinfo.profile%20https://www.googleapis.com/auth/userinfo.email&state=some-state")) - } -} From 9559fc128f5280b9d5bdbab8765c2d1142723825 Mon Sep 17 00:00:00 2001 From: Daniel Teleginski Camargo Date: Wed, 28 Sep 2022 14:38:16 -0600 Subject: [PATCH 2/6] wip --- src/main/kotlin/com/acme/ReflectionConfig.kt | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/acme/ReflectionConfig.kt b/src/main/kotlin/com/acme/ReflectionConfig.kt index 672532d..e16ad30 100644 --- a/src/main/kotlin/com/acme/ReflectionConfig.kt +++ b/src/main/kotlin/com/acme/ReflectionConfig.kt @@ -1,11 +1,15 @@ package com.acme import com.google.api.client.auth.oauth2.* +import com.google.api.client.auth.openidconnect.IdToken +import com.google.api.client.googleapis.auth.oauth2.* +import com.google.api.client.json.webtoken.JsonWebToken import io.quarkus.runtime.annotations.RegisterForReflection @Suppress("unused") @RegisterForReflection( targets = [ + // Google OAuth client AuthorizationCodeRequestUrl::class, AuthorizationCodeResponseUrl::class, AuthorizationCodeTokenRequest::class, @@ -14,7 +18,16 @@ import io.quarkus.runtime.annotations.RegisterForReflection RefreshTokenRequest::class, TokenErrorResponse::class, TokenRequest::class, - TokenResponse::class + TokenResponse::class, + // Google api client + GoogleAuthorizationCodeRequestUrl::class, + GoogleBrowserClientRequestUrl::class, + GoogleClientSecrets::class, + GoogleClientSecrets.Details::class, + GoogleIdToken.Payload::class, + GoogleTokenResponse::class, + IdToken.Payload::class, + JsonWebToken.Payload::class ] ) class ReflectionConfig From 6defe74116add0aefe273643aaa36ab6dac51ea8 Mon Sep 17 00:00:00 2001 From: Daniel Camargo Date: Wed, 28 Sep 2022 14:40:45 -0600 Subject: [PATCH 3/6] chore: adding sample env.json --- pom.xml | 3 +-- sam/env.json.example | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 0fde0f8..6aaf0c2 100644 --- a/pom.xml +++ b/pom.xml @@ -196,16 +196,15 @@ - - + native diff --git a/sam/env.json.example b/sam/env.json.example index 1b49a84..4094853 100644 --- a/sam/env.json.example +++ b/sam/env.json.example @@ -1,5 +1,5 @@ { - "function": { + "GoogleAuthNative": { "GOOGLE_CLIENT_ID": "", "GOOGLE_CLIENT_SECRET": "", "GOOGLE_CLIENT_REDIRECT_URI": "http://localhost:8080/callback" From 53b7e9d3a750d928e3028acd330c29cf417d1081 Mon Sep 17 00:00:00 2001 From: Daniel Camargo Date: Thu, 29 Sep 2022 11:49:16 -0600 Subject: [PATCH 4/6] wip --- README.md | 6 +- pom.xml | 8 +-- sam/env.json.example | 1 + sam/events/callback.json | 2 +- sam/sam.native.yaml | 1 + src/main/kotlin/com/acme/AuthCredential.kt | 6 -- src/main/kotlin/com/acme/GoogleOAuthClient.kt | 61 +++++++------------ .../kotlin/com/acme/GoogleOAuthProperties.kt | 2 +- src/main/kotlin/com/acme/ReflectionConfig.kt | 60 +++++++++++------- .../com/acme/resources/CallbackResource.kt | 2 +- src/main/resources/application.properties | 5 -- src/main/resources/application.yml | 9 +++ 12 files changed, 78 insertions(+), 85 deletions(-) delete mode 100644 src/main/kotlin/com/acme/AuthCredential.kt delete mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/application.yml diff --git a/README.md b/README.md index e7f8dbb..a55ad78 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,9 @@ mvn clean package -Pnative Running AWS SAM locally ```bash -# Make sure you have your "sam/env.json" file in place -sam local invoke --template sam/sam.native.yaml --event sam/events/authorize.json +sam local invoke --template sam/sam.native.yaml --env-vars sam/env.json --event sam/events/authorize.json ``` ```bash -# Make sure you have your "sam/env.json" file in place -sam local invoke --template sam/sam.native.yaml --event sam/events/callback.json +sam local invoke --template sam/sam.native.yaml --env-vars sam/env.json --event sam/events/callback.json ``` diff --git a/pom.xml b/pom.xml index 6aaf0c2..89ff68e 100644 --- a/pom.xml +++ b/pom.xml @@ -46,10 +46,6 @@ io.quarkus quarkus-arc - - io.quarkus - quarkus-grpc - io.quarkus quarkus-resteasy-reactive @@ -78,6 +74,10 @@ org.jboss.logging commons-logging-jboss-logging + + io.quarkus + quarkus-config-yaml + diff --git a/sam/env.json.example b/sam/env.json.example index 4094853..c4f11dd 100644 --- a/sam/env.json.example +++ b/sam/env.json.example @@ -1,5 +1,6 @@ { "GoogleAuthNative": { + "LOG_LEVEL": "DEBUG", "GOOGLE_CLIENT_ID": "", "GOOGLE_CLIENT_SECRET": "", "GOOGLE_CLIENT_REDIRECT_URI": "http://localhost:8080/callback" diff --git a/sam/events/callback.json b/sam/events/callback.json index e5ab5e2..304d283 100644 --- a/sam/events/callback.json +++ b/sam/events/callback.json @@ -1,4 +1,4 @@ { - "path": "/callback?...", + "path": "/callback?state=some-state&code=4%2F0ARtbsJq5sVmW60sVxVWpKemSD3uUpbYjkAEpDIEU7L9bXZ1xGB1UQR_UI5zaNEM-g_xJgw&scope=email+profile+openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&authuser=0&prompt=consent", "httpMethod": "GET" } diff --git a/sam/sam.native.yaml b/sam/sam.native.yaml index 8473b6a..b5ba0d3 100644 --- a/sam/sam.native.yaml +++ b/sam/sam.native.yaml @@ -20,6 +20,7 @@ Resources: Environment: Variables: DISABLE_SIGNAL_HANDLERS: true + LOG_LEVEL: null GOOGLE_CLIENT_ID: null GOOGLE_CLIENT_SECRET: null GOOGLE_CLIENT_REDIRECT_URI: null diff --git a/src/main/kotlin/com/acme/AuthCredential.kt b/src/main/kotlin/com/acme/AuthCredential.kt deleted file mode 100644 index aab5115..0000000 --- a/src/main/kotlin/com/acme/AuthCredential.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.acme - -import com.google.api.client.auth.oauth2.BearerToken -import com.google.api.client.auth.oauth2.Credential - -class AuthCredential : Credential(BearerToken.authorizationHeaderAccessMethod()) diff --git a/src/main/kotlin/com/acme/GoogleOAuthClient.kt b/src/main/kotlin/com/acme/GoogleOAuthClient.kt index 97ef778..b9cfe3d 100644 --- a/src/main/kotlin/com/acme/GoogleOAuthClient.kt +++ b/src/main/kotlin/com/acme/GoogleOAuthClient.kt @@ -1,8 +1,11 @@ package com.acme -import com.google.api.client.auth.oauth2.TokenResponse -import com.google.api.client.googleapis.auth.oauth2.* -import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport +import com.google.api.client.auth.oauth2.BearerToken +import com.google.api.client.auth.oauth2.Credential +import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow +import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeRequestUrl +import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest +import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets import com.google.api.client.http.apache.v2.ApacheHttpTransport import com.google.api.client.json.JsonFactory import com.google.api.client.json.gson.GsonFactory @@ -70,59 +73,37 @@ class GoogleOAuthClient( .newTokenRequest(authorizationCode) .setRedirectUri(properties.redirectUri()) - logger.info("Entries {}", codeTokenRequest.entries.size) + logger.warn("Entries {}", codeTokenRequest.entries.size) + codeTokenRequest.entries.forEach { e -> + logger.info("${e.key} = ${e.value}") + } + + logger.info("Let's execute the request") val token = codeTokenRequest.execute() // Let's save the refresh token in DynamoDB so that every instance will // be able to reuse it val httpTransport = ApacheHttpTransport() val jsonFactory: JsonFactory = GsonFactory.getDefaultInstance() - val credential = AuthCredential() - credential.accessToken = token.accessToken - val oauth2 = Oauth2 - .Builder(httpTransport, jsonFactory, credential) + val httpRequestInitializer = Credential.Builder(BearerToken.authorizationHeaderAccessMethod()).build() + httpRequestInitializer.accessToken = token.accessToken + logger.info("httpRequestInitializer.accessToken {}", httpRequestInitializer.accessToken) + + val oauth2: Oauth2 = Oauth2 + .Builder(httpTransport, jsonFactory, httpRequestInitializer) .setApplicationName(properties.applicationName()) .build() - val userInfo = oauth2 + val get = oauth2 .userinfo() .get() - .execute() + + val userInfo = get.execute() return AuthUser( refreshToken = token.refreshToken, userInfo = userInfo ) } - - // A new cache will be created for each instance. In other words, each instance will - // resolve a new access token every 59 minutes and 55 seconds. - fun getAuthCredential(refreshToken: String): AuthCredential { - logger.debug("Fetching a new access token") - val httpTransport = GoogleNetHttpTransport.newTrustedTransport() - - val jsonFactory: JsonFactory = GsonFactory.getDefaultInstance() - - val tokenResponse: TokenResponse = GoogleRefreshTokenRequest( - httpTransport, - jsonFactory, - refreshToken, - properties.clientId(), - properties.clientSecret() - ) - .setScopes(scopes) - .setGrantType("refresh_token") - .execute() - - // Instantiate a credential - val credential = AuthCredential() - credential.accessToken = tokenResponse.accessToken - - return credential - } - - fun applicationName(): String { - return properties.applicationName() - } } diff --git a/src/main/kotlin/com/acme/GoogleOAuthProperties.kt b/src/main/kotlin/com/acme/GoogleOAuthProperties.kt index c7acb32..5093b7f 100644 --- a/src/main/kotlin/com/acme/GoogleOAuthProperties.kt +++ b/src/main/kotlin/com/acme/GoogleOAuthProperties.kt @@ -12,6 +12,6 @@ interface GoogleOAuthProperties { fun redirectUri(): String companion object { - const val PREFIX = "google" + const val PREFIX = "pws-google" } } diff --git a/src/main/kotlin/com/acme/ReflectionConfig.kt b/src/main/kotlin/com/acme/ReflectionConfig.kt index e16ad30..e0fef01 100644 --- a/src/main/kotlin/com/acme/ReflectionConfig.kt +++ b/src/main/kotlin/com/acme/ReflectionConfig.kt @@ -1,33 +1,47 @@ package com.acme -import com.google.api.client.auth.oauth2.* -import com.google.api.client.auth.openidconnect.IdToken -import com.google.api.client.googleapis.auth.oauth2.* -import com.google.api.client.json.webtoken.JsonWebToken import io.quarkus.runtime.annotations.RegisterForReflection @Suppress("unused") @RegisterForReflection( targets = [ - // Google OAuth client - AuthorizationCodeRequestUrl::class, - AuthorizationCodeResponseUrl::class, - AuthorizationCodeTokenRequest::class, - AuthorizationRequestUrl::class, - PasswordTokenRequest::class, - RefreshTokenRequest::class, - TokenErrorResponse::class, - TokenRequest::class, - TokenResponse::class, - // Google api client - GoogleAuthorizationCodeRequestUrl::class, - GoogleBrowserClientRequestUrl::class, - GoogleClientSecrets::class, - GoogleClientSecrets.Details::class, - GoogleIdToken.Payload::class, - GoogleTokenResponse::class, - IdToken.Payload::class, - JsonWebToken.Payload::class + // google-http-client-1.42.2.jar + com.google.api.client.http.HttpHeaders::class, + com.google.api.client.json.rpc2.JsonRpcRequest::class, + com.google.api.client.json.webtoken.JsonWebSignature::class, + com.google.api.client.json.webtoken.JsonWebToken.Header::class, + // google-api-client:2.0.0 + com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeRequestUrl::class, + com.google.api.client.googleapis.auth.oauth2.GoogleBrowserClientRequestUrl::class, + com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets::class, + com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets.Details::class, + com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload::class, + com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse::class, + com.google.api.client.googleapis.json.GoogleJsonError.ErrorInfo::class, + com.google.api.client.googleapis.json.GoogleJsonError.Details::class, + com.google.api.client.googleapis.json.GoogleJsonError.ParameterViolations::class, + com.google.api.client.googleapis.json.GoogleJsonError::class, + com.google.api.client.googleapis.json.GoogleJsonErrorContainer::class, + com.google.api.client.googleapis.mtls.ContextAwareMetadataJson::class, + // google-oauth-client:1.34.1 + com.google.api.client.auth.oauth.OAuthAuthorizeTemporaryTokenUrl::class, + com.google.api.client.auth.oauth.OAuthCallbackUrl::class, + com.google.api.client.auth.oauth.OAuthCredentialsResponse::class, + com.google.api.client.auth.oauth2.AuthorizationCodeRequestUrl::class, + com.google.api.client.auth.oauth2.AuthorizationCodeResponseUrl::class, + com.google.api.client.auth.oauth2.AuthorizationCodeTokenRequest::class, + com.google.api.client.auth.oauth2.AuthorizationRequestUrl::class, + com.google.api.client.auth.oauth2.PasswordTokenRequest::class, + com.google.api.client.auth.oauth2.RefreshTokenRequest::class, + com.google.api.client.auth.oauth2.TokenErrorResponse::class, + com.google.api.client.auth.oauth2.TokenRequest::class, + com.google.api.client.auth.oauth2.TokenResponse::class, + com.google.api.client.auth.openidconnect.IdToken.Payload::class, + com.google.api.client.auth.openidconnect.IdTokenResponse::class, + // @TODO: This must be fixed first + // https://github.com/googleapis/google-oauth-java-client/issues/947 + // com.google.api.client.auth.openidconnect.IdTokenVerifier.PublicKeyLoader.JsonWebKeySet::class + // com.google.api.client.auth.openidconnect.IdTokenVerifier.PublicKeyLoader.JsonWebKey::class ] ) class ReflectionConfig diff --git a/src/main/kotlin/com/acme/resources/CallbackResource.kt b/src/main/kotlin/com/acme/resources/CallbackResource.kt index 0e7611c..706561d 100644 --- a/src/main/kotlin/com/acme/resources/CallbackResource.kt +++ b/src/main/kotlin/com/acme/resources/CallbackResource.kt @@ -23,7 +23,7 @@ class CallbackResource( @QueryParam("code") code: String, @QueryParam("state") state: String ): String { - logger.debug("Get refresh token") + logger.info("Get refresh token") val (refreshToken) = googleOAuthClient.authenticate(code) return refreshToken diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index 331947e..0000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1,5 +0,0 @@ -google.application-name="Some app" -google.client-id=${GOOGLE_CLIENT_ID} -google.client-secret=${GOOGLE_CLIENT_SECRET} -google.redirect-uri=${GOOGLE_CLIENT_REDIRECT_URI} -quarkus.log.level=INFO diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..c2a736b --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,9 @@ +pws-google: + application-name: "Some app" + client-id: ${GOOGLE_CLIENT_ID} + client-secret: ${GOOGLE_CLIENT_SECRET} + redirect-uri: ${GOOGLE_CLIENT_REDIRECT_URI} + +quarkus: + log: + level: ${LOG_LEVEL} From 928e2db39acf87da264218d58e17f7b2a0e67b9f Mon Sep 17 00:00:00 2001 From: Daniel Teleginski Camargo Date: Thu, 29 Sep 2022 11:51:09 -0600 Subject: [PATCH 5/6] wip --- src/main/kotlin/com/acme/ReflectionConfig.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/com/acme/ReflectionConfig.kt b/src/main/kotlin/com/acme/ReflectionConfig.kt index e0fef01..ca91c1f 100644 --- a/src/main/kotlin/com/acme/ReflectionConfig.kt +++ b/src/main/kotlin/com/acme/ReflectionConfig.kt @@ -5,12 +5,12 @@ import io.quarkus.runtime.annotations.RegisterForReflection @Suppress("unused") @RegisterForReflection( targets = [ - // google-http-client-1.42.2.jar + // google-http-client com.google.api.client.http.HttpHeaders::class, com.google.api.client.json.rpc2.JsonRpcRequest::class, com.google.api.client.json.webtoken.JsonWebSignature::class, com.google.api.client.json.webtoken.JsonWebToken.Header::class, - // google-api-client:2.0.0 + // google-api-client com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeRequestUrl::class, com.google.api.client.googleapis.auth.oauth2.GoogleBrowserClientRequestUrl::class, com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets::class, @@ -23,7 +23,7 @@ import io.quarkus.runtime.annotations.RegisterForReflection com.google.api.client.googleapis.json.GoogleJsonError::class, com.google.api.client.googleapis.json.GoogleJsonErrorContainer::class, com.google.api.client.googleapis.mtls.ContextAwareMetadataJson::class, - // google-oauth-client:1.34.1 + // google-oauth-client com.google.api.client.auth.oauth.OAuthAuthorizeTemporaryTokenUrl::class, com.google.api.client.auth.oauth.OAuthCallbackUrl::class, com.google.api.client.auth.oauth.OAuthCredentialsResponse::class, From c5d3efa6175776327c4f7c6d17f20d6e6cd4b630 Mon Sep 17 00:00:00 2001 From: Daniel Teleginski Camargo Date: Thu, 29 Sep 2022 12:48:52 -0600 Subject: [PATCH 6/6] wip --- src/main/kotlin/com/acme/ReflectionConfig.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/acme/ReflectionConfig.kt b/src/main/kotlin/com/acme/ReflectionConfig.kt index ca91c1f..b2f084a 100644 --- a/src/main/kotlin/com/acme/ReflectionConfig.kt +++ b/src/main/kotlin/com/acme/ReflectionConfig.kt @@ -23,6 +23,13 @@ import io.quarkus.runtime.annotations.RegisterForReflection com.google.api.client.googleapis.json.GoogleJsonError::class, com.google.api.client.googleapis.json.GoogleJsonErrorContainer::class, com.google.api.client.googleapis.mtls.ContextAwareMetadataJson::class, + // google-api-services-oauth2-v2 + com.google.api.services.oauth2.model.Userinfo::class, + com.google.api.services.oauth2.model.Tokeninfo::class, + com.google.api.services.oauth2.Oauth2.Tokeninfo::class, + com.google.api.services.oauth2.Oauth2.Userinfo.Get::class, + com.google.api.services.oauth2.Oauth2.Userinfo.V2.Me::class, + com.google.api.services.oauth2.Oauth2.Userinfo.V2.Me.Get::class, // google-oauth-client com.google.api.client.auth.oauth.OAuthAuthorizeTemporaryTokenUrl::class, com.google.api.client.auth.oauth.OAuthCallbackUrl::class, @@ -37,7 +44,7 @@ import io.quarkus.runtime.annotations.RegisterForReflection com.google.api.client.auth.oauth2.TokenRequest::class, com.google.api.client.auth.oauth2.TokenResponse::class, com.google.api.client.auth.openidconnect.IdToken.Payload::class, - com.google.api.client.auth.openidconnect.IdTokenResponse::class, + com.google.api.client.auth.openidconnect.IdTokenResponse::class // @TODO: This must be fixed first // https://github.com/googleapis/google-oauth-java-client/issues/947 // com.google.api.client.auth.openidconnect.IdTokenVerifier.PublicKeyLoader.JsonWebKeySet::class