Verifying JWT signatures using JWK (JSON Web Keys) in Go involves several steps. Below are detailed steps and examples illustrating how to implement this functionality in Go.
Step 1: Import Required Packages
First, import the necessary packages for handling JWT and JWK. The github.com/dgrijalva/jwt-go library is a popular choice for JWT handling, while github.com/lestrrat-go/jwx can be used for JWK.
goimport ( "github.com/dgrijalva/jwt-go" "github.com/lestrrat-go/jwx/jwk" )
Step 2: Load JWK from URL
Typically, a JWK set can be obtained from a public URL. Use the jwk.Fetch function to load the JWK set from the URL.
gourl := "https://example.com/.well-known/jwks.json" set, err := jwk.Fetch(url) if err != nil { // Handle error }
Step 3: Parse JWT and Extract Header
Before verifying the signature, parse the JWT to extract its header, particularly the kid (Key ID) attribute, which is necessary for selecting the correct key from the JWK set.
gotokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" token, _ := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { // Skip signature verification return nil, nil }) if token == nil { // Handle error } // Extract kid kid := token.Header["kid"].(string)
Step 4: Select Correct JWK Based on kid
Use the kid obtained from the JWT header to select the correct key from the JWK set.
gokeys := set.LookupKeyID(kid) if len(keys) == 0 { // Handle error: no key found return } var key interface{} if err := keys[0].Raw(&key); err != nil { // Handle error return }
Step 5: Verify JWT Signature
Finally, use the key obtained from the JWK set to verify the JWT signature.
gotoken, err = jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { return key, nil }) if err != nil { // Handle error, e.g., signature mismatch return } if !token.Valid { // Token is invalid return }
Complete Example
Integrating the above steps into a function creates a complete application for verifying JWT signatures.
gofunc verifyJWT(tokenString string, jwksUrl string) (bool, error) { set, err := jwk.Fetch(jwksUrl) if err != nil { return false, err } token, _ := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { kid := token.Header["kid"].(string) keys := set.LookupKeyID(kid) if len(keys) == 0 { return nil, fmt.Errorf("no key found for kid: %s", kid) } var key interface{} if err := keys[0].Raw(&key); err != nil { return nil, err } return key, nil }) if err != nil { return false, err } return token.Valid, nil }
This function encapsulates the process of loading keys from the JWK set, parsing the JWT, and verifying the signature. You can test it for different scenarios by changing the values of tokenString and jwksUrl.