1

First of all, I have an oauth2 authorization server developed with Spring Boot, annotated with @EnableAuthorizationServer and extending AuthorizationServerConfigurerAdapter. Same way there is a resource server annotated with @EnableResourceServer extending ResourceServerConfigurerAdapter.

Scenario: Android app with Google login must call my backend REST API that is the resource server in a secured way. The Android app must call the REST API to get logged user information to populate app interface using supplied access tokens generated by my authorization server. Trying to integrate the app with the resource and authorization servers and Google login I found the following Google developers page: https://developers.google.com/identity/sign-in/web/backend-auth

There they say:

If you use Google Sign-In with an app or site that communicates with a backend server, you might need to identify the currently signed-in user on the server. To do so securely, after a user successfully signs in, send the user's ID token to your server using HTTPS. Then, on the server, verify the integrity of the ID token and use the user information contained in the token to establish a session or create a new account.

Ok, I can add an endpoint to receive the ID token, no problem.

Other session, in the end of the same page also says:

After you have verified the token, check if the user is already in your user database. If so, establish an authenticated session for the user. If the user isn't yet in your user database, create a new user record from the information in the ID token payload, and establish a session for the user. You can prompt the user for any additional profile information you require when you detect a newly created user in your app.

Here we have the point. After verifying the ID token sent by the Android app during user login with Google, how can I integrate the app with the API and generate access and refresh token on my Spring authorization server to send them to the mobile app?

lcs_godoy
  • 88
  • 1
  • 11

1 Answers1

1

Found solution: I've created a custom grant type. This way I can override authentication to check ID token with provider (in this case, Google) and if it's OK, let the normal flow do its job.

Based my implementation on:

1) Authorization server: https://github.com/spring-projects/spring-security-oauth/blob/master/tests/annotation/custom-grant/src/main/java/demo/Application.java

2) Custom token granter: https://github.com/spring-projects/spring-security-oauth/blob/master/tests/annotation/custom-grant/src/main/java/demo/CustomTokenGranter.java:

Here is my custom implementation in Kotlin:

1) Override configure(endpoints: AuthorizationServerEndpointsConfigurer) from AuthorizationServerConfigurerAdapter:

override fun configure(endpoints: AuthorizationServerEndpointsConfigurer) {
    endpoints
        .tokenStore(tokenStore)
        .reuseRefreshTokens(false)
        .accessTokenConverter(accessTokenConverter)
        .authenticationManager(authenticationManager)
        .userDetailsService(authUserDetails)
        .tokenGranter(tokenGranter(endpoints))
}

private fun tokenGranter(endpoints: AuthorizationServerEndpointsConfigurer) =
    CompositeTokenGranter(mutableListOf(endpoints.tokenGranter).apply {
        add(GoogleTokenGranter(
                tokenServices = endpoints.tokenServices,
                clientDetailsService = endpoints.clientDetailsService,
                requestFactory = endpoints.oAuth2RequestFactory,
                grantType = GoogleTokenGranter.GRANT_TYPE
            )
        )
    })

2) Custom token granter implementation:

class GoogleTokenGranter internal constructor(
    tokenServices: AuthorizationServerTokenServices,
    clientDetailsService: ClientDetailsService,
    requestFactory: OAuth2RequestFactory,
    grantType: String
) : AbstractTokenGranter(tokenServices, clientDetailsService, requestFactory, grantType) {

    override fun getOAuth2Authentication(client: ClientDetails, tokenRequest: TokenRequest): OAuth2Authentication {

        // check token with google here

        return super.getOAuth2Authentication(client, tokenRequest)
    }

    companion object {
        const val GRANT_TYPE = "google"
    }
}

Client mobile app must send a request this way:

POST /oauth/token

Payload:

{
  "grant_type": "google"
  "username": "admin"
  "token": "xxxxxxxxxxxxxxxxxxxxx" 
}

Don't forget to send also the authorization header with your client credentials to Spring validate allowed grand types, scopes, etc

lcs_godoy
  • 88
  • 1
  • 11