package auth import ( "crypto/rsa" "fmt" "os" ) // LoadTokenConfig assembles a TokenConfig from PEM files on disk: one private // signing key (privateKeyPath, identified by signKID) and a set of public keys // (publicKeyPaths: kid -> path) accepted for verification, supporting key // rotation. The signing key's public part is added automatically if its kid is // absent from publicKeyPaths. func LoadTokenConfig(privateKeyPath, signKID string, publicKeyPaths map[string]string) (TokenConfig, error) { if privateKeyPath == "" { return TokenConfig{}, fmt.Errorf("auth: JWT private key path is empty") } if signKID == "" { return TokenConfig{}, fmt.Errorf("auth: JWT key id (kid) is empty") } privPEM, err := os.ReadFile(privateKeyPath) if err != nil { return TokenConfig{}, fmt.Errorf("auth: read private key: %w", err) } priv, err := LoadPrivateKeyPEM(privPEM) if err != nil { return TokenConfig{}, err } verify := map[string]*rsa.PublicKey{} for kid, path := range publicKeyPaths { pubPEM, err := os.ReadFile(path) if err != nil { return TokenConfig{}, fmt.Errorf("auth: read public key %q: %w", kid, err) } pub, err := LoadPublicKeyPEM(pubPEM) if err != nil { return TokenConfig{}, fmt.Errorf("auth: parse public key %q: %w", kid, err) } verify[kid] = pub } return TokenConfig{ SignKey: priv, SignKID: signKID, VerifyKeys: verify, }, nil }