Problem
I am trying to use the New Google Sign-In API for authenticating with my own backend server. Every call to my server contains the idToken jwt as a Bearer token. On the backend side, I validate the token by checking the signature, and checking if the token is expired or not. The problem is that the New Google Sign-In API returns jwts with 1 hour expiration time, but I could not find any way to refresh the token.
Questions
- Is this a valid/safe way to authenticate the user with my backend server?
- How do I get a fresh id token without the user having to go through the sign-in flow again?
Code
How I get the idToken client side
fun showSignInDialog() {
val request = GetSignInIntentRequest.builder()
.setServerClientId(GOOGLE_SERVER_CLIENT_ID)
.build()
Identity.getSignInClient(context)
.getSignInIntent(request)
.addOnSuccessListener {
googleSignInLauncher.launch(IntentSenderRequest.Builder(it).build())
}
}
fun handleSignInResult(result: ActivityResult) {
val credential = Identity.getSignInClient(context)
.getSignInCredentialFromIntent(result.data)
// later this token will be used for querying user-related data on my backend
setToken(credential.googleIdToken)
}
How I validate my endpoints on the backend side
fun Application.installAuthentication() {
val googleJwtIssuer = environment.config.property("jwt.google.domain").getString()
val googleJwtAudience = environment.config.property("jwt.google.audience").getString()
val jwtRealm = environment.config.property("jwt.google.realm").getString()
val googleJwkProvider = JwkProviderBuilder(URL("https://www.googleapis.com/oauth2/v3/certs"))
.cached(10, 24, TimeUnit.HOURS)
.rateLimited(10, 1, TimeUnit.MINUTES)
.build()
install(Authentication) {
jwt("google") {
verifier(googleJwkProvider) {
withIssuer(googleJwtIssuer)
withAudience(googleJwtAudience)
}
realm = jwtRealm
validate { credentials ->
if (credentials.payload.audience.contains(googleJwtAudience))
JWTPrincipal(credentials.payload)
else
null
}
}
}
}
// in my main
installAuthentication()
routing {
authenticate("google") {
get("/get-todos") {
val payload = call.principal<JWTPrincipal>()?.payload ?: error("JWTPrincipal not found")
call.respond(getTodos(payload.subject))
}
}
}
Edit
Sample JWT returned by the google sign in API (on first sign in)
{
"iss": "https://accounts.google.com",
"azp": "secret",
"aud": "secret",
"sub": "secret",
"email": "secret@gmail.com",
"email_verified": true,
"name": "Ben Jerry",
"picture": "https://lh3.googleusercontent.com/secret",
"given_name": "Patrik",
"family_name": "Aradi",
"locale": "en-GB",
"iat": 1616243551,
"exp": 1616247151
}
It does not have a refresh token in it unfortunatelly