2
votes

I’m implementing Google login in a Go script and I’m stuck trying to verify the ID Token has been signed by Google.

Specifically I’m trying to do the first bullet point in here.

I’ve obtained the token and split it into its header, payload and signature. But I don’t know how to use Google’s public key to verify the signature.

I saw that generating signature involves a HMACSHA256 algorithm, but I don’t quite know what to do with the JWK or PEM keys that Google provides. I’m currently doing it manually due to certain restrictions.

Does anyone know how I can verify the signature please?

1
Is this fully manual as in no external imports?Muhamed Keta
I'm allowed some imports, but those are limited to those that are already in the repo. Which is why I'm trying to do it completely manual. I stumbled on to this post, but I am unable to make use of oauth2/v2 unfortunately.Ryan T.
Theoretically you can always copy and paste those parts of the code and use that (following proper copyright laws and everything), but that sounds a lot like finding a loop hole, which might not be what you want. But on the other hand a good rule of thumb is to not reinvent the wheel especially when it comes to cryptography. I will be looking into it however.Muhamed Keta
You can check how jwt libs do it, e.g. github.com/dgrijalva/jwt-go.icza
You have to create the message digest (say digest 1) from the payload using the public key shared by google. Also, need to decrypt the signature using the same key to get another digest (say digest 2). At last, compare both to verify. Read this to understander how signing works - coderjony.com/blogs/…Ankush Jain

1 Answers

2
votes

Looking at the php example on https://github.com/googleapis/google-api-php-client/blob/2fb6e702aca5d68203fa737f89f6f774022494c6/src/AccessToken/Verify.php#L90, it appears that Google API signs the tokens using RS256(RSA Signature with SHA-256).

The following code should verify the signature and parse the token. There is no need to split the token in your code. jwt.RSACheck(token []byte, key *rsa.PublicKey) (*jwt.Claims, error) in the "github.com/pascaldekloe/jwt" package would do all the heavy lifting for you.

keyLocation is the Google public key stored locally

package main

import "github.com/pascaldekloe/jwt"

//jwtValidator verifies and parses the JWT Token
func jwtValidator(token string, keyLocation string) (j *jwtToken, err error) {
    token := []byte(token)

    pubK, err := rsaPublicKey(keyLocation)
    if err != nil {
        return
    }

    // parses the token only if signature is valid
    claims, err := jwt.RSACheck(token, pubK)
    if err != nil {
        err = fmt.Errorf("token signature invalid %s", err)
        return
    }
    if !claims.Valid(time.Now()) {
        err = fmt.Errorf("token time constraints exceeded")
        return
    }

    j = &jwtToken{
        id:      claims.ID,
        issuer:  claims.Issuer,
        issued:  claims.Issued,
        subject: claims.Subject,
        expires: claims.Expires,
    }

    return
}