mirror of
https://github.com/jeremyd/ergo.git
synced 2026-08-14 01:04:51 -07:00
08d8145f26
Includes two compatibility breaks: * New, incompatible configuration format for extjwt * JWTs accepted with IRCV3BEARER MUST have an expiration time
106 lines
2.3 KiB
Go
106 lines
2.3 KiB
Go
// Copyright (c) 2020 Daniel Oaks <daniel@danieloaks.net>
|
|
// Copyright (c) 2020 Shivaram Lingamneni <slingamn@cs.stanford.edu>
|
|
// released under the MIT license
|
|
|
|
package jwt
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
jwt "github.com/golang-jwt/jwt/v5"
|
|
)
|
|
|
|
var (
|
|
ErrNoKeys = errors.New("No EXTJWT signing keys are enabled")
|
|
)
|
|
|
|
type MapClaims jwt.MapClaims
|
|
|
|
type JwtServiceConfig struct {
|
|
Expiration time.Duration
|
|
Description string
|
|
URL string `yaml:"url"`
|
|
Algorithm string `yaml:"algorithm"`
|
|
KeyString string `yaml:"key"`
|
|
KeyFile string `yaml:"key-file"`
|
|
signingMethod jwt.SigningMethod
|
|
signingKey any
|
|
verifyKey any
|
|
}
|
|
|
|
func (t *JwtServiceConfig) Postprocess() (err error) {
|
|
if t.Algorithm == "" {
|
|
// disabled
|
|
return
|
|
}
|
|
|
|
var keyBytes []byte
|
|
if t.KeyFile != "" {
|
|
keyBytes, err = os.ReadFile(t.KeyFile)
|
|
if err != nil {
|
|
return
|
|
}
|
|
} else if t.KeyString != "" {
|
|
keyBytes = []byte(t.KeyString)
|
|
} else {
|
|
return ErrNoKeys
|
|
}
|
|
|
|
switch strings.ToLower(t.Algorithm) {
|
|
case "hmac":
|
|
t.signingKey = keyBytes
|
|
t.verifyKey = keyBytes
|
|
t.signingMethod = jwt.SigningMethodHS256
|
|
case "rsa":
|
|
rsaPrivkey, err := jwt.ParseRSAPrivateKeyFromPEM(keyBytes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
t.signingKey = rsaPrivkey
|
|
t.verifyKey = rsaPrivkey.Public()
|
|
t.signingMethod = jwt.SigningMethodRS256
|
|
case "eddsa":
|
|
ecPrivkey, err := jwt.ParseEdPrivateKeyFromPEM(keyBytes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
t.signingKey = ecPrivkey
|
|
ed25519PrivKey, ok := ecPrivkey.(ed25519.PrivateKey)
|
|
if !ok {
|
|
// impossible due to golang-jwt enforcement:
|
|
return errors.New("unexpected non-ed25519 private key found")
|
|
}
|
|
t.verifyKey = ed25519PrivKey.Public()
|
|
t.signingMethod = jwt.SigningMethodEdDSA
|
|
default:
|
|
return fmt.Errorf("invalid JWT algorithm: %s", t.Algorithm)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (t *JwtServiceConfig) Enabled() bool {
|
|
return t.Expiration != 0 && t.signingMethod != nil
|
|
}
|
|
|
|
func (t *JwtServiceConfig) verifyKeyFunc(_ *jwt.Token) (key any, err error) {
|
|
return t.verifyKey, nil
|
|
}
|
|
|
|
func (t *JwtServiceConfig) SignEXTJWT(claims MapClaims) (result string, err error) {
|
|
if !t.Enabled() {
|
|
err = ErrNoKeys
|
|
return
|
|
}
|
|
|
|
claims["exp"] = time.Now().Unix() + int64(t.Expiration/time.Second)
|
|
|
|
token := jwt.NewWithClaims(t.signingMethod, jwt.MapClaims(claims))
|
|
return token.SignedString(t.signingKey)
|
|
}
|