auth
API
auth
packageAPI reference for the auth
package.
Imports
(18)
INT
github.com/mirkobrombin/go-foundation/v2/core/contracts
STD
bytes
STD
crypto
STD
crypto/ecdsa
STD
crypto/ed25519
STD
crypto/elliptic
STD
crypto/hmac
STD
crypto/rand
STD
crypto/rsa
STD
crypto/sha256
STD
encoding/base64
STD
encoding/json
STD
errors
STD
fmt
STD
math/big
STD
strings
STD
time
STD
testing
S
Payload
Payload holds the claims for a simple token.
core/auth/token.go:34-37
type Payload struct
Methods
Valid
Method
Returns
error
func (Payload) Valid() error
{
if time.Now().Unix() >= p.Exp {
return ErrExpiredToken
}
return nil
}
Fields
| Name | Type | Description |
|---|---|---|
| Sub | string | json:"sub" |
| Exp | int64 | json:"exp" |
S
StandardClaims
StandardClaims holds common JWT-like claims.
core/auth/token.go:40-47
type StandardClaims struct
Methods
Valid
Method
Returns
error
func (StandardClaims) Valid() error
{
if time.Now().Unix() >= c.Exp {
return ErrExpiredToken
}
return nil
}
Fields
| Name | Type | Description |
|---|---|---|
| Sub | string | json:"sub" |
| Exp | int64 | json:"exp" |
| Iat | int64 | json:"iat,omitempty" |
| Jti | string | json:"jti,omitempty" |
| Iss | string | json:"iss,omitempty" |
| Aud | string | json:"aud,omitempty" |
I
interface
Claims
Claims validates token claims.
core/auth/token.go:50-52
type Claims interface
Methods
T
type
Algorithm
Algorithm identifies a signing algorithm.
core/auth/token.go:69-69
type Algorithm string
F
function
SignToken
SignToken signs a payload with HMAC-SHA256.
Parameters
p
secret
[]byte
Returns
string
error
core/auth/token.go:83-88
func SignToken(p Payload, secret []byte) (string, error)
{
if err := ValidateHMACSecret(secret); err != nil {
return "", err
}
return sign(p, secret, AlgHMACSHA256)
}
Uses
F
function
VerifyToken
VerifyToken verifies and decodes a HMAC-SHA256 token.
Parameters
token
string
secret
[]byte
Returns
error
core/auth/token.go:91-96
func VerifyToken(token string, secret []byte) (Payload, error)
{
if err := ValidateHMACSecret(secret); err != nil {
return Payload{}, err
}
return verify[Payload](token, secret, AlgHMACSHA256)
}
Uses
F
function
ValidateHMACSecret
ValidateHMACSecret checks the minimum key size for HMAC-SHA256.
Parameters
secret
[]byte
Returns
error
core/auth/token.go:99-104
func ValidateHMACSecret(secret []byte) error
{
if len(secret) < sha256.Size {
return fmt.Errorf("auth: HMAC secret must be at least %d bytes", sha256.Size)
}
return nil
}
S
struct
Key
Key holds signing key material.
core/auth/token.go:107-113
type Key struct
Fields
| Name | Type | Description |
|---|---|---|
| ID | string | |
| Secret | []byte | |
| Algorithm | Algorithm | |
| Private | crypto.PrivateKey | |
| Public | crypto.PublicKey |
Uses
S
struct
SignedToken
SignedToken holds a token string with its key ID and raw payload.
core/auth/token.go:116-121
type SignedToken struct
Fields
| Name | Type | Description |
|---|---|---|
| Token | string | |
| KeyID | string | |
| Payload | []byte | |
| Raw | string |
I
interface
TokenService
TokenService signs and verifies tokens.
core/auth/token.go:124-127
type TokenService interface
Methods
S
multiKeyService
core/auth/token.go:129-131
type multiKeyService struct
Methods
Sign
Method
Parameters
claims
Claims
Returns
error
func (*multiKeyService) Sign(claims Claims) (*SignedToken, error)
{
if len(s.keys) == 0 {
return nil, errors.New("auth: no keys configured")
}
k := s.keys[0]
return signWithKey(claims, k)
}
Verify
Method
Parameters
token
string
Returns
error
func (*multiKeyService) Verify(token string) (Claims, error)
{
for _, k := range s.keys {
claims, err := verifyWithKey[StandardClaims](token, k)
if err == nil {
return claims, nil
}
if errors.Is(err, ErrUnknownKeyID) {
continue
}
}
return nil, ErrInvalidSignature
}
Fields
| Name | Type | Description |
|---|---|---|
| keys | []Key |
F
function
NewService
NewService creates a validated TokenService with one or more keys.
Parameters
keys
...Key
Returns
error
core/auth/token.go:136-158
func NewService(keys ...Key) (TokenService, error)
{
if len(keys) == 0 {
return nil, errors.New("auth: at least one key is required")
}
ids := make(map[string]struct{}, len(keys))
for _, key := range keys {
if key.ID == "" {
return nil, errors.New("auth: key ID cannot be empty")
}
if _, exists := ids[key.ID]; exists {
return nil, fmt.Errorf("auth: duplicate key ID %q", key.ID)
}
ids[key.ID] = struct{}{}
if err := validateKey(key); err != nil {
return nil, fmt.Errorf("auth: key %q: %w", key.ID, err)
}
}
cloned := make([]Key, len(keys))
for index, key := range keys {
cloned[index] = cloneKey(key)
}
return &multiKeyService{keys: cloned}, nil
}
Uses
F
function
cloneKey
core/auth/token.go:160-199
func cloneKey(key Key) Key
{
cloned := key
cloned.Secret = append([]byte(nil), key.Secret...)
switch key.Algorithm {
case AlgRS256:
private := key.Private.(*rsa.PrivateKey)
privateClone := &rsa.PrivateKey{
PublicKey: rsa.PublicKey{N: new(big.Int).Set(private.N), E: private.E},
D: new(big.Int).Set(private.D),
Primes: make([]*big.Int, len(private.Primes)),
}
for index, prime := range private.Primes {
privateClone.Primes[index] = new(big.Int).Set(prime)
}
privateClone.Precompute()
public := key.Public.(*rsa.PublicKey)
cloned.Private = privateClone
cloned.Public = &rsa.PublicKey{N: new(big.Int).Set(public.N), E: public.E}
case AlgES256:
private := key.Private.(*ecdsa.PrivateKey)
public := key.Public.(*ecdsa.PublicKey)
cloned.Private = &ecdsa.PrivateKey{
PublicKey: ecdsa.PublicKey{
Curve: private.Curve,
X: new(big.Int).Set(private.X),
Y: new(big.Int).Set(private.Y),
},
D: new(big.Int).Set(private.D),
}
cloned.Public = &ecdsa.PublicKey{
Curve: public.Curve,
X: new(big.Int).Set(public.X),
Y: new(big.Int).Set(public.Y),
}
case AlgEdDSA:
cloned.Private = append(ed25519.PrivateKey(nil), key.Private.(ed25519.PrivateKey)...)
cloned.Public = append(ed25519.PublicKey(nil), key.Public.(ed25519.PublicKey)...)
}
return cloned
}
Uses
F
function
MustNewService
MustNewService creates a TokenService or panics on invalid key configuration.
Parameters
keys
...Key
Returns
core/auth/token.go:202-208
func MustNewService(keys ...Key) TokenService
{
service, err := NewService(keys...)
if err != nil {
panic(err)
}
return service
}
Uses
F
function
validateKey
Parameters
key
Returns
error
core/auth/token.go:210-262
func validateKey(key Key) error
{
switch key.Algorithm {
case AlgHMACSHA256:
return ValidateHMACSecret(key.Secret)
case AlgRS256:
private, privateOK := key.Private.(*rsa.PrivateKey)
public, publicOK := key.Public.(*rsa.PublicKey)
if !privateOK || private == nil || !publicOK || public == nil {
return errors.New("RS256 requires RSA private and public keys")
}
if err := private.Validate(); err != nil {
return fmt.Errorf("invalid RSA private key: %w", err)
}
if private.N.BitLen() < 2048 {
return errors.New("RS256 requires an RSA key of at least 2048 bits")
}
if private.PublicKey.E != public.E || private.PublicKey.N.Cmp(public.N) != 0 {
return errors.New("RSA private and public keys do not match")
}
case AlgES256:
private, privateOK := key.Private.(*ecdsa.PrivateKey)
public, publicOK := key.Public.(*ecdsa.PublicKey)
if !privateOK || private == nil || !publicOK || public == nil {
return errors.New("ES256 requires ECDSA private and public keys")
}
if private.Curve != elliptic.P256() || public.Curve != elliptic.P256() {
return errors.New("ES256 requires the P-256 curve")
}
if private.D == nil || private.D.Sign() <= 0 || private.D.Cmp(private.Curve.Params().N) >= 0 {
return errors.New("invalid ECDSA private scalar")
}
derivedX, derivedY := private.Curve.ScalarBaseMult(private.D.Bytes())
if private.X == nil || private.Y == nil || public.X == nil || public.Y == nil ||
derivedX.Cmp(private.X) != 0 || derivedY.Cmp(private.Y) != 0 ||
derivedX.Cmp(public.X) != 0 || derivedY.Cmp(public.Y) != 0 {
return errors.New("ECDSA private and public keys do not match")
}
case AlgEdDSA:
private, privateOK := key.Private.(ed25519.PrivateKey)
public, publicOK := key.Public.(ed25519.PublicKey)
if !privateOK || len(private) != ed25519.PrivateKeySize ||
!publicOK || len(public) != ed25519.PublicKeySize {
return errors.New("EdDSA requires Ed25519 private and public keys")
}
derived := private.Public().(ed25519.PublicKey)
if !bytes.Equal(derived, public) {
return errors.New("Ed25519 private and public keys do not match")
}
default:
return fmt.Errorf("unsupported algorithm %q", key.Algorithm)
}
return nil
}
Uses
F
function
signWithKey
Returns
error
core/auth/token.go:285-322
func signWithKey(claims Claims, key Key) (*SignedToken, error)
{
data, err := json.Marshal(claims)
if err != nil {
return nil, err
}
var sig []byte
switch key.Algorithm {
case AlgHMACSHA256:
sig, err = signHMAC(data, key.Secret)
case AlgRS256:
sig, err = signRSA(data, key.Private.(*rsa.PrivateKey))
case AlgES256:
sig, err = signECDSA(data, key.Private.(*ecdsa.PrivateKey))
case AlgEdDSA:
sig, err = signEd25519(data, key.Private.(ed25519.PrivateKey))
default:
return nil, fmt.Errorf("auth: unsupported algorithm: %s", key.Algorithm)
}
if err != nil {
return nil, err
}
kid := base64.RawURLEncoding.EncodeToString([]byte(key.ID))
payload := base64.RawURLEncoding.EncodeToString(data)
signature := base64.RawURLEncoding.EncodeToString(sig)
token := kid + "." + payload + "." + signature
if len(token) > maxTokenSize {
return nil, fmt.Errorf("auth: signed token exceeds %d bytes", maxTokenSize)
}
return &SignedToken{
Token: token,
KeyID: key.ID,
Payload: data,
Raw: token,
}, nil
}
Uses
F
function
verifyWithKey
Parameters
token
string
key
Returns
T
error
core/auth/token.go:324-381
func verifyWithKey[T Claims](token string, key Key) (T, error)
{
var zero T
parts, ok := splitToken(token)
if !ok {
return zero, ErrInvalidToken
}
kidBytes, err := base64.RawURLEncoding.DecodeString(parts[0])
if err != nil {
return zero, ErrInvalidToken
}
if string(kidBytes) != key.ID {
return zero, ErrUnknownKeyID
}
payloadBytes, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return zero, ErrInvalidToken
}
sig, err := base64.RawURLEncoding.DecodeString(parts[2])
if err != nil {
return zero, ErrInvalidToken
}
switch key.Algorithm {
case AlgRS256:
if err := verifyRSA(payloadBytes, sig, key.Public.(*rsa.PublicKey)); err != nil {
return zero, err
}
case AlgHMACSHA256:
if !verifyHMAC(payloadBytes, sig, key.Secret) {
return zero, ErrInvalidSignature
}
case AlgES256:
if !verifyECDSA(payloadBytes, sig, key.Public.(*ecdsa.PublicKey)) {
return zero, ErrInvalidSignature
}
case AlgEdDSA:
if !verifyEd25519(payloadBytes, sig, key.Public.(ed25519.PublicKey)) {
return zero, ErrInvalidSignature
}
default:
return zero, fmt.Errorf("auth: unsupported algorithm: %s", key.Algorithm)
}
var claims T
if err := json.Unmarshal(payloadBytes, &claims); err != nil {
return zero, err
}
if c, ok := any(&claims).(Claims); ok {
if err := c.Valid(); err != nil {
return zero, err
}
}
return claims, nil
}
Uses
F
function
sign
Parameters
Returns
string
error
core/auth/token.go:383-402
func sign[T any](p T, secret []byte, alg Algorithm) (string, error)
{
data, err := json.Marshal(p)
if err != nil {
return "", err
}
var sig []byte
switch alg {
case AlgHMACSHA256:
sig = computeSig(data, secret)
default:
return "", fmt.Errorf("auth: unsupported algorithm: %s", alg)
}
token := base64.RawURLEncoding.EncodeToString(data) + "." + base64.RawURLEncoding.EncodeToString(sig)
if len(token) > maxTokenSize {
return "", fmt.Errorf("auth: signed token exceeds %d bytes", maxTokenSize)
}
return token, nil
}
Uses
F
function
verify
Parameters
Returns
T
error
core/auth/token.go:404-449
func verify[T any](token string, secret []byte, alg Algorithm) (T, error)
{
var zero T
if len(token) == 0 || len(token) > maxTokenSize {
return zero, ErrInvalidToken
}
payload, signature, ok := strings.Cut(token, ".")
if !ok || payload == "" || signature == "" || strings.Contains(signature, ".") {
return zero, ErrInvalidToken
}
payloadBytes, err := base64.RawURLEncoding.DecodeString(payload)
if err != nil {
return zero, ErrInvalidToken
}
sig, err := base64.RawURLEncoding.DecodeString(signature)
if err != nil {
return zero, ErrInvalidToken
}
var valid bool
switch alg {
case AlgHMACSHA256:
expected := computeSig(payloadBytes, secret)
valid = hmac.Equal(sig, expected)
default:
return zero, fmt.Errorf("auth: unsupported algorithm: %s", alg)
}
if !valid {
return zero, ErrInvalidSignature
}
if err := json.Unmarshal(payloadBytes, &zero); err != nil {
return zero, err
}
if p, ok := any(zero).(Claims); ok {
if err := p.Valid(); err != nil {
return zero, err
}
}
return zero, nil
}
Uses
F
function
signHMAC
Parameters
data
[]byte
secret
[]byte
Returns
[]byte
error
core/auth/token.go:451-453
func signHMAC(data, secret []byte) ([]byte, error)
{
return computeSig(data, secret), nil
}
F
function
signRSA
Parameters
data
[]byte
key
Returns
[]byte
error
core/auth/token.go:455-458
func signRSA(data []byte, key *rsa.PrivateKey) ([]byte, error)
{
hash := sha256.Sum256(data)
return rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, hash[:])
}
F
function
signECDSA
Parameters
data
[]byte
key
Returns
[]byte
error
core/auth/token.go:460-463
func signECDSA(data []byte, key *ecdsa.PrivateKey) ([]byte, error)
{
hash := sha256.Sum256(data)
return ecdsa.SignASN1(rand.Reader, key, hash[:])
}
F
function
signEd25519
Parameters
data
[]byte
Returns
[]byte
error
core/auth/token.go:465-467
func signEd25519(data []byte, key ed25519.PrivateKey) ([]byte, error)
{
return ed25519.Sign(key, data), nil
}
F
function
verifyRSA
Parameters
Returns
error
core/auth/token.go:469-472
func verifyRSA(data, sig []byte, key *rsa.PublicKey) error
{
hash := sha256.Sum256(data)
return rsa.VerifyPKCS1v15(key, crypto.SHA256, hash[:], sig)
}
F
function
verifyECDSA
Parameters
Returns
bool
core/auth/token.go:474-477
func verifyECDSA(data, sig []byte, key *ecdsa.PublicKey) bool
{
hash := sha256.Sum256(data)
return ecdsa.VerifyASN1(key, hash[:], sig)
}
F
function
verifyEd25519
Parameters
Returns
bool
core/auth/token.go:479-481
func verifyEd25519(data, sig []byte, key ed25519.PublicKey) bool
{
return ed25519.Verify(key, data, sig)
}
F
function
verifyHMAC
Parameters
data
[]byte
sig
[]byte
secret
[]byte
Returns
bool
core/auth/token.go:483-486
func verifyHMAC(data, sig, secret []byte) bool
{
expected := computeSig(data, secret)
return hmac.Equal(sig, expected)
}
F
function
computeSig
Parameters
payload
[]byte
secret
[]byte
Returns
[]byte
core/auth/token.go:488-492
func computeSig(payload, secret []byte) []byte
{
h := hmac.New(sha256.New, secret)
_, _ = h.Write(payload)
return h.Sum(nil)
}
F
function
splitToken
Parameters
token
string
Returns
[3]string
bool
core/auth/token.go:494-510
func splitToken(token string) ([3]string, bool)
{
var parts [3]string
if len(token) == 0 || len(token) > maxTokenSize {
return parts, false
}
var ok bool
parts[0], token, ok = strings.Cut(token, ".")
if !ok {
return parts, false
}
parts[1], parts[2], ok = strings.Cut(token, ".")
if !ok || strings.Contains(parts[2], ".") ||
parts[0] == "" || parts[1] == "" || parts[2] == "" {
return parts, false
}
return parts, true
}
F
function
hmacSecret
Parameters
value
byte
Returns
[]byte
core/auth/token_test.go:19-21
func hmacSecret(value byte) []byte
{
return bytes.Repeat([]byte{value}, 32)
}
F
function
TestSignTokenAndVerifyToken
Parameters
t
core/auth/token_test.go:23-36
func TestSignTokenAndVerifyToken(t *testing.T)
{
secret := testHMACSecret
token, err := SignToken(Payload{Sub: "u1", Exp: time.Now().Add(time.Minute).Unix()}, secret)
if err != nil {
t.Fatalf("SignToken() error = %v", err)
}
payload, err := VerifyToken(token, secret)
if err != nil {
t.Fatalf("VerifyToken() error = %v", err)
}
if payload.Sub != "u1" {
t.Fatalf("VerifyToken() subject = %q, want %q", payload.Sub, "u1")
}
}
F
function
TestVerifyTokenRejectsMalformedToken
Parameters
t
core/auth/token_test.go:38-43
func TestVerifyTokenRejectsMalformedToken(t *testing.T)
{
_, err := VerifyToken("invalid", testHMACSecret)
if !errors.Is(err, ErrInvalidToken) {
t.Fatalf("VerifyToken() error = %v, want ErrInvalidToken", err)
}
}
F
function
TestVerifyTokenRejectsInvalidSignature
Parameters
t
core/auth/token_test.go:45-60
func TestVerifyTokenRejectsInvalidSignature(t *testing.T)
{
secret := testHMACSecret
token, err := SignToken(Payload{Sub: "u1", Exp: time.Now().Add(time.Minute).Unix()}, secret)
if err != nil {
t.Fatalf("SignToken() error = %v", err)
}
payload, _, ok := strings.Cut(token, ".")
if !ok {
t.Fatal("expected 2 parts")
}
tampered := payload + "." + base64.RawURLEncoding.EncodeToString([]byte("tampered"))
_, err = VerifyToken(tampered, secret)
if !errors.Is(err, ErrInvalidSignature) {
t.Fatalf("VerifyToken() error = %v, want ErrInvalidSignature", err)
}
}
F
function
TestVerifyTokenRejectsExpiredToken
Parameters
t
core/auth/token_test.go:62-72
func TestVerifyTokenRejectsExpiredToken(t *testing.T)
{
secret := testHMACSecret
token, err := SignToken(Payload{Sub: "u1", Exp: time.Now().Add(-time.Minute).Unix()}, secret)
if err != nil {
t.Fatalf("SignToken() error = %v", err)
}
_, err = VerifyToken(token, secret)
if !errors.Is(err, ErrExpiredToken) {
t.Fatalf("VerifyToken() error = %v, want ErrExpiredToken", err)
}
}
F
function
TestNewService_KeyRotation
Parameters
t
core/auth/token_test.go:74-93
func TestNewService_KeyRotation(t *testing.T)
{
oldKey := Key{ID: "old", Secret: hmacSecret('o'), Algorithm: AlgHMACSHA256}
newKey := Key{ID: "new", Secret: hmacSecret('n'), Algorithm: AlgHMACSHA256}
svc := MustNewService(newKey, oldKey)
token, err := svc.Sign(&StandardClaims{Sub: "user1", Exp: time.Now().Add(time.Hour).Unix()})
if err != nil {
t.Fatalf("Sign failed: %v", err)
}
if token.KeyID != "new" {
t.Errorf("got key id %q, want %q", token.KeyID, "new")
}
claims, err := svc.Verify(token.Token)
if err != nil {
t.Fatalf("Verify failed: %v", err)
}
c := claims.(StandardClaims)
if c.Sub != "user1" {
t.Errorf("got sub %s, want user1", c.Sub)
}
}
F
function
TestNewServiceClonesHMACKeyMaterial
Parameters
t
core/auth/token_test.go:95-110
func TestNewServiceClonesHMACKeyMaterial(t *testing.T)
{
secret := hmacSecret('k')
service := MustNewService(Key{ID: "key", Secret: secret, Algorithm: AlgHMACSHA256})
secret[0] ^= 0xff
token, err := service.Sign(&StandardClaims{
Sub: "user",
Exp: time.Now().Add(time.Minute).Unix(),
})
if err != nil {
t.Fatal(err)
}
if _, err := service.Verify(token.Token); err != nil {
t.Fatalf("Verify() after caller key mutation: %v", err)
}
}
F
function
TestNewService_KeyRotationOldKeyStillWorks
Parameters
t
core/auth/token_test.go:112-126
func TestNewService_KeyRotationOldKeyStillWorks(t *testing.T)
{
oldKey := Key{ID: "old", Secret: hmacSecret('o'), Algorithm: AlgHMACSHA256}
newKey := Key{ID: "new", Secret: hmacSecret('n'), Algorithm: AlgHMACSHA256}
svcOld := MustNewService(oldKey)
token, _ := svcOld.Sign(&StandardClaims{Sub: "user1", Exp: time.Now().Add(time.Hour).Unix()})
svcNew := MustNewService(newKey, oldKey)
claims, err := svcNew.Verify(token.Token)
if err != nil {
t.Fatalf("Verify with old key should still work: %v", err)
}
c := claims.(StandardClaims)
if c.Sub != "user1" {
t.Errorf("got sub %s, want user1", c.Sub)
}
}
F
function
TestNewService_WrongKey
Parameters
t
core/auth/token_test.go:128-138
func TestNewService_WrongKey(t *testing.T)
{
key1 := Key{ID: "k1", Secret: hmacSecret('1'), Algorithm: AlgHMACSHA256}
key2 := Key{ID: "k2", Secret: hmacSecret('2'), Algorithm: AlgHMACSHA256}
svc1 := MustNewService(key1)
token, _ := svc1.Sign(&StandardClaims{Sub: "u", Exp: time.Now().Add(time.Hour).Unix()})
svc2 := MustNewService(key2)
_, err := svc2.Verify(token.Token)
if err == nil {
t.Error("expected error when verifying with wrong key")
}
}
F
function
TestNewService_RS256
Parameters
t
core/auth/token_test.go:140-155
func TestNewService_RS256(t *testing.T)
{
priv, _ := rsa.GenerateKey(rand.Reader, 2048)
svc := MustNewService(Key{ID: "rsa1", Private: priv, Public: &priv.PublicKey, Algorithm: AlgRS256})
token, err := svc.Sign(&StandardClaims{Sub: "user_rsa", Exp: time.Now().Add(time.Hour).Unix()})
if err != nil {
t.Fatalf("Sign with RSA failed: %v", err)
}
claims, err := svc.Verify(token.Token)
if err != nil {
t.Fatalf("Verify with RSA failed: %v", err)
}
c := claims.(StandardClaims)
if c.Sub != "user_rsa" {
t.Errorf("got sub %s, want user_rsa", c.Sub)
}
}
F
function
TestNewService_EdDSA
Parameters
t
core/auth/token_test.go:157-175
func TestNewService_EdDSA(t *testing.T)
{
pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("GenerateKey failed: %v", err)
}
svc := MustNewService(Key{ID: "ed1", Private: priv, Public: pub, Algorithm: AlgEdDSA})
token, err := svc.Sign(&StandardClaims{Sub: "user_ed", Exp: time.Now().Add(time.Hour).Unix()})
if err != nil {
t.Fatalf("Sign with EdDSA failed: %v", err)
}
claims, err := svc.Verify(token.Token)
if err != nil {
t.Fatalf("Verify with EdDSA failed: %v", err)
}
c := claims.(StandardClaims)
if c.Sub != "user_ed" {
t.Errorf("got sub %s, want user_ed", c.Sub)
}
}
F
function
TestNewService_ECDSA
Parameters
t
core/auth/token_test.go:177-195
func TestNewService_ECDSA(t *testing.T)
{
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("GenerateKey failed: %v", err)
}
svc := MustNewService(Key{ID: "ec1", Private: priv, Public: &priv.PublicKey, Algorithm: AlgES256})
token, err := svc.Sign(&StandardClaims{Sub: "user_ec", Exp: time.Now().Add(time.Hour).Unix()})
if err != nil {
t.Fatalf("Sign with ECDSA failed: %v", err)
}
claims, err := svc.Verify(token.Token)
if err != nil {
t.Fatalf("Verify with ECDSA failed: %v", err)
}
c := claims.(StandardClaims)
if c.Sub != "user_ec" {
t.Errorf("got sub %s, want user_ec", c.Sub)
}
}
F
function
TestStandardClaims_Valid_Expired
Parameters
t
core/auth/token_test.go:197-202
func TestStandardClaims_Valid_Expired(t *testing.T)
{
c := StandardClaims{Sub: "u", Exp: time.Now().Add(-time.Hour).Unix()}
if err := c.Valid(); err == nil {
t.Error("expected error for expired claims")
}
}
F
function
TestStandardClaims_Valid_NotExpired
Parameters
t
core/auth/token_test.go:204-209
func TestStandardClaims_Valid_NotExpired(t *testing.T)
{
c := StandardClaims{Sub: "u", Exp: time.Now().Add(time.Hour).Unix()}
if err := c.Valid(); err != nil {
t.Errorf("unexpected error: %v", err)
}
}
F
function
TestNewService_NoKeys
Parameters
t
core/auth/token_test.go:211-215
func TestNewService_NoKeys(t *testing.T)
{
if _, err := NewService(); err == nil {
t.Error("NewService() accepted no keys")
}
}
F
function
TestNewService_RejectsInvalidKeys
Parameters
t
core/auth/token_test.go:217-233
func TestNewService_RejectsInvalidKeys(t *testing.T)
{
tests := []Key{
{ID: "short", Secret: []byte("short"), Algorithm: AlgHMACSHA256},
{ID: "rsa", Algorithm: AlgRS256, Private: "wrong", Public: "wrong"},
{ID: "unknown", Algorithm: Algorithm("unknown")},
}
for _, key := range tests {
if _, err := NewService(key); err == nil {
t.Fatalf("NewService() accepted key %#v", key)
}
}
key := Key{ID: "same", Secret: testHMACSecret, Algorithm: AlgHMACSHA256}
if _, err := NewService(key, key); err == nil {
t.Fatal("NewService() accepted duplicate key IDs")
}
}
F
function
TestNewService_RejectsMismatchedAsymmetricKeys
Parameters
t
core/auth/token_test.go:235-276
func TestNewService_RejectsMismatchedAsymmetricKeys(t *testing.T)
{
rsaPrivate, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatal(err)
}
otherRSA, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatal(err)
}
ecdsaPrivate, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
otherECDSA, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
_, edPrivate, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
otherEdPublic, _, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
p384Private, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
if err != nil {
t.Fatal(err)
}
keys := []Key{
{ID: "rsa", Private: rsaPrivate, Public: &otherRSA.PublicKey, Algorithm: AlgRS256},
{ID: "ecdsa", Private: ecdsaPrivate, Public: &otherECDSA.PublicKey, Algorithm: AlgES256},
{ID: "ed25519", Private: edPrivate, Public: otherEdPublic, Algorithm: AlgEdDSA},
{ID: "p384", Private: p384Private, Public: &p384Private.PublicKey, Algorithm: AlgES256},
}
for _, key := range keys {
if _, err := NewService(key); err == nil {
t.Fatalf("NewService() accepted mismatched key %q", key.ID)
}
}
}
F
function
TestClaimsRejectExactExpirationSecond
Parameters
t
core/auth/token_test.go:278-286
func TestClaimsRejectExactExpirationSecond(t *testing.T)
{
expiration := time.Now().Unix()
if err := (Payload{Exp: expiration}).Valid(); !errors.Is(err, ErrExpiredToken) {
t.Fatalf("Payload.Valid() error = %v, want ErrExpiredToken", err)
}
if err := (StandardClaims{Exp: expiration}).Valid(); !errors.Is(err, ErrExpiredToken) {
t.Fatalf("StandardClaims.Valid() error = %v, want ErrExpiredToken", err)
}
}
F
function
TestSignTokenRejectsShortSecret
Parameters
t
core/auth/token_test.go:288-292
func TestSignTokenRejectsShortSecret(t *testing.T)
{
if _, err := SignToken(Payload{}, []byte("short")); err == nil {
t.Fatal("SignToken() accepted a short HMAC secret")
}
}
F
function
TestVerifyRejectsOversizedTokens
Parameters
t
core/auth/token_test.go:294-311
func TestVerifyRejectsOversizedTokens(t *testing.T)
{
oversized := strings.Repeat(".", maxTokenSize+1)
if _, err := VerifyToken(oversized, testHMACSecret); !errors.Is(err, ErrInvalidToken) {
t.Fatalf("VerifyToken() error = %v, want ErrInvalidToken", err)
}
service, err := NewService(Key{
ID: "key",
Secret: testHMACSecret,
Algorithm: AlgHMACSHA256,
})
if err != nil {
t.Fatal(err)
}
if _, err := service.Verify(oversized); err == nil {
t.Fatal("TokenService.Verify() accepted an oversized token")
}
}
F
function
TestTokenServiceDoesNotCreateUnverifiableOversizedToken
Parameters
t
core/auth/token_test.go:321-333
func TestTokenServiceDoesNotCreateUnverifiableOversizedToken(t *testing.T)
{
service, err := NewService(Key{
ID: "key",
Secret: testHMACSecret,
Algorithm: AlgHMACSHA256,
})
if err != nil {
t.Fatal(err)
}
if _, err := service.Sign(largeClaims{Data: strings.Repeat("x", maxTokenSize)}); err == nil {
t.Fatal("Sign() created a token larger than the verifier accepts")
}
}