Skip to content

Instantly share code, notes, and snippets.

@MaciejKaras
Last active September 17, 2021 14:33
Show Gist options
  • Save MaciejKaras/1f867a1cedd4500b6304085b66f5f5df to your computer and use it in GitHub Desktop.
Save MaciejKaras/1f867a1cedd4500b6304085b66f5f5df to your computer and use it in GitHub Desktop.
Google tokenId validation example using "google.golang.org/api/idtoken" from Google Client API
package auth
import (
"context"
"errors"
"fmt"
"google.golang.org/api/idtoken"
"time"
)
type tokenVerifier struct {
clockSkew time.Duration
validator *idtoken.Validator
audience string
validIssuers []string
validSubjects []string
}
type TokenVerifier interface {
VerifyIdToken(ctx context.Context, idToken string) error
}
func New(ctx context.Context, audience string, serviceAccountNumbers ...string) (TokenVerifier, error) {
validator, err := idtoken.NewValidator(ctx)
if err != nil {
return nil, err
}
return &tokenVerifier{
clockSkew: time.Minute * 5,
validator: validator,
audience: audience,
validIssuers: []string{"accounts.google.com", "https://accounts.google.com"},
validSubjects: serviceAccountNumbers,
}, nil
}
func (v *tokenVerifier) VerifyIdToken(ctx context.Context, idToken string) error {
payload, err := v.validator.Validate(ctx, idToken, v.audience)
if err != nil {
return err
}
if err := v.verifyTimeWindow(payload.IssuedAt, payload.Expires); err != nil {
return err
}
if err := v.verifyIssuer(payload.Issuer); err != nil {
return err
}
if err := v.verifySubject(payload.Subject); err != nil {
return err
}
return nil
}
func (v *tokenVerifier) verifyTimeWindow(issuedAt int64, expires int64) error {
if issuedAt < 1 {
return errors.New("no issuedAt time in token")
}
if expires < 1 {
return errors.New("no expires time in token")
}
now := time.Now()
clockSkew := int64(v.clockSkew.Seconds())
earliest := issuedAt - clockSkew
latest := expires + clockSkew
if now.Unix() < earliest {
return errors.New("token sent before issuedAt value")
}
if now.Unix() > latest {
return errors.New("token sent after expires value")
}
return nil
}
func (v *tokenVerifier) verifyIssuer(tokenIssuer string) error {
for _, issuer := range v.validIssuers {
if issuer == tokenIssuer {
return nil
}
}
return fmt.Errorf("wrong issuer: %s", tokenIssuer)
}
func (v *tokenVerifier) verifySubject(tokenSubject string) error {
for _, subject := range v.validSubjects {
if subject == tokenSubject {
return nil
}
}
return fmt.Errorf("wrong subject: %s", tokenSubject)
}
@videate-miguel
Copy link

Do you have an example of how to use it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment