Introduction
When working with JSON Web Tokens (JWTs) in Go, especially those issued by Okta, validating the signature can be a bit challenging due to the use of JSON Web Keys (JWKs). This article will walk you through the process of decoding and validating a JWT's signature using Go.
Understanding JWT and JWK
JWTs are compact, URL-safe tokens that represent claims to be transferred between two parties. Okta uses JWKs to sign these tokens, which means you need to retrieve the appropriate public key to verify the signature.
Steps to Validate a JWT Signature
Retrieve the JWKs: You can obtain the JWKs from your Okta authorization server. The endpoint typically looks like this:
https://{yourOktaDomain}/oauth2/v1/keysDecode the JWT: Use a JWT library in Go to decode the JWT. This will give you access to the header and payload.
Verify the Signature: Using the JWK corresponding to the JWT's
kid(Key ID), verify the signature of the JWT.
Example Code
Here’s a simple example demonstrating how to validate a JWT in Go:
package main
import (
"encoding/json"
"fmt"
"net/http"
"github.com/dgrijalva/jwt-go"
)
// JWK structure to hold the keys
type JWK struct {
Keys []struct {
Alg string `json:"alg"`
E string `json:"e"`
N string `json:"n"`
Kid string `json:"kid"`
Kty string `json:"kty"`
Use string `json:"use"`
} `json:"keys"`
}
func main() {
// Example JWT
tokenString := "eyJhbGciOiJSUzI1NiIsImtpZCI6..."
// Fetch JWKs from Okta
resp, err := http.Get("https://{yourOktaDomain}/oauth2/v1/keys")
if err != nil {
fmt.Println("Error fetching JWKs:", err)
return
}
defer resp.Body.Close()
var jwk JWK
json.NewDecoder(resp.Body).Decode(&jwk)
// Decode the JWT
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
// Validate the algorithm
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
// Find the key by kid
kid := token.Header["kid"].(string)
for _, key := range jwk.Keys {
if key.Kid == kid {
// Convert JWK to RSA public key
return jwt.ParseRSAPublicKeyFromPEM([]byte(key.N))
}
}
return nil, fmt.Errorf("key not found")
})
if err != nil {
fmt.Println("Error validating token:", err)
return
}
// Token is valid
fmt.Println("Token is valid:", token)
}
Conclusion
Validating JWT signatures in Go using Okta's JWKs involves fetching the keys, decoding the JWT, and verifying the signature against the appropriate key. By following the steps outlined in this guide, you can ensure that your application securely validates JWTs issued by Okta.