OneTap for Android and Micronaut

Daniel Llewellyn
4 min readJul 2, 2020

One tap is goggles newest cross platform identity service and its implementation is very neat for a user

Our aim will be to setup OneTap for Android and verify the user with a micronaut backend.

Account setup

First head here to create or configure an existing project

https://console.developers.google.com/

First, setup the oauth consent screen

Go to “Credentials” on the left hand side and then ‘Create credentials’ you want to create an ‘oauth client ID’
Fill in your package name, and you can retrieve the SHA-1 fingerprint for your debug certificate using the command below
keytool -keystore ~/.android/debug.keystore -list -v

(Password is android)

Now you need to do the same steps — but create a web application. Leave the defaults. You need this client ID for your app — NOT your android ID.

This will generate a client ID for you — copy this and use it for both your android application and your backend

Setting up the Android application

Add this to your app/build.gradle file:

implementation 'com.google.android.gms:play-services-auth:18.0.0
implementation "androidx.fragment:fragment:1.3.0-alpha06"
implementation "androidx.fragment:fragment-ktx:1.3.0-alpha06"

Add the following to strings.xml placing the client ID from your create account steps you need the one from your web app — not from you android app.

<string name="one_tap_client_id"></string>

I’ve created a wrapper around the callbacks to make it more kotlin coroutines friendly:

First, sign up a user. We need to create a signup request

and we can perform the first stage of the request like this

So the next step will be requesting and then handling the result, we have included the alpha fragment library so we could use the new API for retrieving activity results

So inside our signup we want to do this:

signUpResult.launch(
IntentSenderRequest.Builder(result.pendingIntent)
.build()
)

Leaving our full implementation of signup as this

Naturally, the flow should be to sign someone in if they already have an account, so we’ll follow a similar flow to sign up

And our handler

You might have noticed the ‘error handler’ function which looks like this; and for our login if it fails it will try to sign up a user.

private fun errorHandler(onError: (() -> Unit)? = null): CoroutineExceptionHandler {
return CoroutineExceptionHandler { _, e ->
onError?.invoke()
Log.e(
TAG,
e.localizedMessage ?: "",
e
)
}
}

Altogether our class now looks like this

For the sake of this article — I’m going to take that token and manually finish the authentication when it’s printed. Change

Log.d(TAG, "Got ID token.")

to

Log.d(TAG, "Got ID token. $token")

What you want to do in production will differ — but the basic flow should be to take that token, and add it to the header “Bearer: Authorization <token>”

Implementing the backend

Create a micronaut project

mn create-application OneTapAuthenticator -l kotlin -f security-jwt

Modify the application.yml to look like this:

micronaut:
security:
enabled: true
endpoints:
login:
enabled: true
token:
jwt:
enabled: true
signatures:
secret:
generator:
secret: "${JWT_GENERATOR_SIGNATURE_SECRET:pleaseChangeThisSecretForANewOne}"

Also, create a controller for verification

mn create-controller CreateCheckerController

Now, add this in order to verify

@Controller("/authenticated")
class CertificateCheckerController {

@Secured(SecurityRule.IS_AUTHENTICATED)
@Get("/verification")
fun verify(principal: Principal?) =
principal

}

Run the application and check that it runs, run

curl http://localhost:8080/authenticated/verification

This should fail with a 401.

Allow authentication in the app

First add the google client API to the gradle file

implementation 'com.google.api-client:google-api-client:1.30.9'

The code is fairly straightforward:

  • The singleton / token validator means this will be used when a client has a Bearer token
  • The token passed in is a JWT — the one that is generated in your android app (idToken)
  • The rest is calling through to the Google client API to verify the certificate. We take the payload and return the authentication response

Run, and check with curl:

curl -H "Authorization: Bearer <your token from the android app>" http://localhost:8080/authenticated/verification  -v

The rest is up to you — I would suggest from here that you generate a JWT from micronaut and store that (securely — in the keystore) in your application. When you launch your app, check if the JWT token exists and is valid — if not, kick off your signin flow again.

Enjoy!

--

--