This should be a simple question but I've been frustrated trying to figure it out.
Given a JWT token and a RSA public/private key pair, how do I convert the token to a struct and verify it?
type JwtData struct {
AccessToken string `json:"access_token"`
Iat int `json:"iat"`
Exp int `json:"exp"`
Sub string `json:"sub"`
Iss string `json:"iss"`
}
privateKeyString := "my_private_key"
publicKeyString := "my_public_key"
tokenString := "my_token_data"
decryptedData, error := whatDoIDoHere(publicKeyString, tokenString)
It seems like this is the "standard" Go library for JWTs (is there even a standard one? I find Go's package ecosystem confusing) https://github.com/lestrrat-go/jwx/tree/main/jwt
The demo covers this fairly well but here is a more complete example showing how to go from an encoded token/certificate (note that the demo generates the cert/jwt first; you can ignore that bit if you are not interested in it).
It seems like this is the "standard" Go library for JWTs (is there even a standard one?)
Go has a Standard Library which covers a lot of basic functionality. I don't think that anything else can be considered 'standard' (this is as per other languages; the Go standard library is pretty comprehensive compared to most). There are a few JWT packages but lestrrat-go/jwx
is fairly comprehensive and, importantly, actively developed.
package main
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"time"
"github.com/lestrrat-go/jwx/jwa"
"github.com/lestrrat-go/jwx/jwk"
"github.com/lestrrat-go/jwx/jwt"
)
func main() {
token, pubKey, err := generateJWT()
if err != nil {
panic(err)
}
// JWT has been generated - lets output it (and the certificate)
fmt.Printf("JWT: %s\nPublic Key: %s", token, pubKey)
parsedToken, err := parseJWT(token, pubKey)
if err != nil {
panic(err)
}
// Standard claims can be accessed as follows:
fmt.Printf("Iss: %v\n", parsedToken.IssuedAt())
fmt.Printf("Exp: %v\n", parsedToken.Expiration())
fmt.Printf("Aud: %v\n", parsedToken.Audience())
// Private Claims
fmt.Printf("Private Claims: %#v\n", parsedToken.PrivateClaims())
}
// generateJWT - Generates a JWT (and signs it with a generated certificate)
func generateJWT() ([]byte, []byte, error) {
// We will start by generating a test token for which a key will be needed
privKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, nil, fmt.Errorf("failed to generate private key: %s\n", err)
}
// Preparation:
// For demonstration purposes, we need to do some preparation
// Create a JWK key to sign the token (and also give a KeyID)
realKey, err := jwk.New(privKey)
if err != nil {
return nil, nil, fmt.Errorf("failed to create JWK: %s\n", err)
}
realKey.Set(jwk.KeyIDKey, `mykey`)
// Create the token
token := jwt.New()
token.Set(`foo`, `bar`)
// Set a few standard keys
token.Set(jwt.IssuedAtKey, time.Now())
token.Set(jwt.ExpirationKey, time.Now().Add(time.Hour)) // lets make it expire in an hour
token.Set(jwt.AudienceKey, "lotsOfUsers")
// Sign the token and generate a payload
signed, err := jwt.Sign(token, jwa.RS256, realKey)
if err != nil {
return nil, nil, fmt.Errorf("failed to generate signed payload: %s\n", err)
}
// For completeness lets encode the public key (so the decoding matches what someone just performing the decode would need to do)
publickey := &privKey.PublicKey
publicKeyBytes, err := x509.MarshalPKIXPublicKey(publickey)
if err != nil {
return nil, nil, fmt.Errorf("error when dumping publickey: %s \n", err)
}
publicKeyBlock := &pem.Block{
Type: "PUBLIC KEY",
Bytes: publicKeyBytes,
}
publicKeyBuff := new(bytes.Buffer)
err = pem.Encode(publicKeyBuff, publicKeyBlock)
if err != nil {
return nil, nil, fmt.Errorf("error when encode public pem: %s \n", err)
}
encodedPubKey := publicKeyBuff.Bytes()
return signed, encodedPubKey, nil
}
// parse the passed in JWT with the passed in certificate and return a map of claims
func parseJWT(signedJWT []byte, encodedPubKey []byte) (jwt.Token, error){
var keyset jwk.Set
privPem, _ := pem.Decode(encodedPubKey)
if privPem == nil {
return nil, fmt.Errorf("Failed to decode PEM block")
}
privPemBytes := privPem.Bytes
var parsedKeyInt interface{}
var err error
if parsedKeyInt, err = x509.ParsePKIXPublicKey(privPemBytes); err != nil {
return nil, fmt.Errorf("Failed to decode cert: %s", err)
}
parsedKey, ok := parsedKeyInt.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("public key not expected type")
}
// Now create a key set that users will use to verity the signed payload against
pubKey, err := jwk.New(parsedKey)
if err != nil {
return nil, fmt.Errorf("failed to create JWK: %s", err)
}
// Remember, the key must have the proper "kid"
pubKey.Set(jwk.KeyIDKey, "mykey")
// This key set contains only one key (the correct one)
keyset = jwk.NewSet()
keyset.Add(pubKey)
parsedToken, err := jwt.Parse(
signedJWT,
// Tell the parser that you want to use this keyset
jwt.WithKeySet(keyset),
// Tell the parser that you can trust this KeySet, and that
// yo want to use the sole key in it
jwt.UseDefaultKey(true),
// We want to validate the token; e.g. it should not have expired
jwt.WithValidate(true),
)
if err != nil {
return nil, fmt.Errorf("failed to parse payload: %s", err)
}
return parsedToken, nil
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.