Create a gist now

Instantly share code, notes, and snippets.

Embed
Verify the JSON Web Token obtained from Firebase Authentication. But now, Go SDK is released by firebase organization.
package jwt
import (
"crypto/rsa"
"errors"
"encoding/json"
"io/ioutil"
"net/http"
"crypto/x509"
"encoding/pem"
"fmt"
"github.com/dgrijalva/jwt-go"
)
// CertsAPIEndpoint is endpoint of getting Public Key.
var CertsAPIEndpoint = "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com"
// GetCertificate is useful for testing.
var GetCertificate = getCertificate
func getCertificates() (certs map[string]string, err error) {
res, err := http.Get(CertsAPIEndpoint)
if err != nil {
return
}
defer res.Body.Close()
data, err := ioutil.ReadAll(res.Body)
if err != nil {
return
}
json.Unmarshal(data, &certs)
return
}
// GetCertificate returns certificate.
func getCertificate(kid string) (cert []byte, err error) {
certs, err := getCertificates()
if err != nil {
return
}
certString := certs[kid]
cert = []byte(certString)
err = nil
return
}
// VerifyJWT to verify the token header, payload and signature.
var VerifyJWT = verifyJWT
// GetCertificateFromToken returns cert from token.
func GetCertificateFromToken(token *jwt.Token) ([]byte, error) {
// Get kid
kid, ok := token.Header["kid"]
if !ok {
return []byte{}, errors.New("kid not found")
}
kidString, ok := kid.(string)
if !ok {
return []byte{}, errors.New("kid cast error to string")
}
return GetCertificate(kidString)
}
// Verify the token payload.
func verifyPayload(t *jwt.Token, projectID string) (ok bool, uid string) {
claims, ok := t.Claims.(jwt.MapClaims)
if !ok {
return
}
// Verify User
claimsAud, ok := claims["aud"].(string)
if claimsAud != projectID || !ok {
return
}
// Verify issued at
iss := "https://securetoken.google.com/" + projectID
claimsIss, ok := claims["iss"].(string)
if claimsIss != iss || !ok {
return
}
// sub is uid of user.
uid, ok = claims["sub"].(string)
if !ok {
return
}
return
}
func readPublicKey(cert []byte) (*rsa.PublicKey, error) {
publicKeyBlock, _ := pem.Decode(cert)
if publicKeyBlock == nil {
return nil, errors.New("invalid public key data")
}
if publicKeyBlock.Type != "CERTIFICATE" {
return nil, fmt.Errorf("invalid public key type: %s", publicKeyBlock.Type)
}
c, err := x509.ParseCertificate(publicKeyBlock.Bytes)
if err != nil {
return nil, err
}
publicKey, ok := c.PublicKey.(*rsa.PublicKey)
if !ok {
return nil, errors.New("not RSA public key")
}
return publicKey, nil
}
func verifyJWT(t, projectID string) (uid string, ok bool) {
parsed, _ := jwt.Parse(t, func(t *jwt.Token) (interface{}, error) {
cert, err := GetCertificateFromToken(t)
if err != nil {
return "", err
}
publicKey, err := readPublicKey(cert)
if err != nil {
return "", err
}
return publicKey, nil
})
ok = parsed.Valid
if !ok {
return
}
// Verify header.
if parsed.Header["alg"] != "RS256" {
ok = false
return
}
// Verify payload.
ok, uid = verifyPayload(parsed, projectID)
return
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment