2

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?

5
  • Is this fully manual as in no external imports? Commented Jan 21, 2021 at 8:52
  • 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.
    Commented Jan 21, 2021 at 8:58
  • 2
    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. Commented Jan 21, 2021 at 9:07
  • You can check how jwt libs do it, e.g. github.com/dgrijalva/jwt-go.
    – icza
    Commented Jan 21, 2021 at 9:22
  • 1
    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/… Commented Jan 21, 2021 at 13:27

1 Answer 1

1

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
}
1
  • Some errors in the code above, and unfortunately i can't use the library, but I'm marking this as the answer as the RSACheck function in the above answer was useful for helping me figure out how to solve the issue.
    – Ryan T.
    Commented Jan 22, 2021 at 1:29

Not the answer you're looking for? Browse other questions tagged or ask your own question.