auth API

auth

package

API reference for the auth package.

S
struct
Implements: Claims

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
struct
Implements: Claims

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

Valid
Method

Returns

error
func Valid(...)
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

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)
}
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)
}
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
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

Sign
Method

Parameters

claims Claims

Returns

error
func Sign(...)
Verify
Method

Parameters

token string

Returns

error
func Verify(...)
S
struct
Implements: TokenService

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
}
F
function

cloneKey

Parameters

key

Returns

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
}
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
}
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
}
F
function

signWithKey

Parameters

claims
key

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
}
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
}
F
function

sign

Parameters

p
T
secret
[]byte
alg

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
}
F
function

verify

Parameters

token
string
secret
[]byte
alg

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
}
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

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

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

data
[]byte
sig
[]byte

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

data
[]byte
sig
[]byte

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

data
[]byte
sig
[]byte

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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")
	}
}
S
struct
Implements: Claims

largeClaims

core/auth/token_test.go:313-315
type largeClaims struct

Methods

Valid
Method

Returns

error
func (largeClaims) Valid() error
{
	return nil
}

Fields

Name Type Description
Data string json:"data"
F
function

TestTokenServiceDoesNotCreateUnverifiableOversizedToken

Parameters

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")
	}
}