In Go, rotating JWT signing keys is essential for maintaining security. This process involves generating a new signing key and updating the JWT creation process to use the new key, while still allowing validation of tokens signed with the old key during a transition period.
Here is an example of how to implement JWT signing key rotation in Go:
// Import necessary packages
import (
"crypto/rsa"
"github.com/dgrijalva/jwt-go"
"time"
"sync"
)
var (
currentKey *rsa.PrivateKey
previousKey *rsa.PrivateKey
mu sync.RWMutex
)
// Function to generate a new RSA key
func generateKey() (*rsa.PrivateKey, error) {
return rsa.GenerateKey(rand.Reader, 2048)
}
// Function to rotate the signing key
func rotateKeys() error {
mu.Lock()
defer mu.Unlock()
newKey, err := generateKey()
if err != nil {
return err
}
// Assign the current key to previous key before rotating
previousKey = currentKey
currentKey = newKey
return nil
}
// Function to sign a JWT
func signToken(userID string) (string, error) {
mu.RLock()
defer mu.RUnlock()
// Use the current key for signing
token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
"userID": userID,
"exp": time.Now().Add(time.Hour * 72).Unix(),
})
return token.SignedString(currentKey)
}
// Function to validate a JWT
func validateToken(tokenString string) (*jwt.Token, error) {
mu.RLock()
defer mu.RUnlock()
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
// Ensure the token is signed with the expected method
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("unexpected signing method")
}
// Return the current key or the previous key for validation
if token.Claims.(jwt.MapClaims)["exp"].(int64) > time.Now().Unix() {
return currentKey.Public(), nil
}
return previousKey.Public(), nil
})
return token, err
}
How do I avoid rehashing overhead with std::set in multithreaded code?
How do I find elements with custom comparators with std::set for embedded targets?
How do I erase elements while iterating with std::set for embedded targets?
How do I provide stable iteration order with std::unordered_map for large datasets?
How do I reserve capacity ahead of time with std::unordered_map for large datasets?
How do I erase elements while iterating with std::unordered_map in multithreaded code?
How do I provide stable iteration order with std::map for embedded targets?
How do I provide stable iteration order with std::map in multithreaded code?
How do I avoid rehashing overhead with std::map in performance-sensitive code?
How do I merge two containers efficiently with std::map for embedded targets?