auth
API
auth
packageAPI reference for the auth
package.
Imports
(14)
S
Payload
Payload holds the claims for a simple token.
pkg/auth/token.go:30-33
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.
pkg/auth/token.go:36-43
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.
pkg/auth/token.go:46-48
type Claims interface
Methods
T
type
Algorithm
Algorithm identifies a signing algorithm.
pkg/auth/token.go:65-65
type Algorithm string
F
function
SignToken
SignToken signs a payload with HMAC-SHA256.
Parameters
p
secret
[]byte
Returns
string
error
pkg/auth/token.go:79-81
func SignToken(p Payload, secret []byte) (string, error)
{
return sign(p, secret, AlgHMACSHA256)
}
Uses
F
function
VerifyToken
VerifyToken verifies and decodes a HMAC-SHA256 token.
Parameters
token
string
secret
[]byte
Returns
error
pkg/auth/token.go:84-86
func VerifyToken(token string, secret []byte) (Payload, error)
{
return verify[Payload](token, secret, AlgHMACSHA256)
}
Uses
S
struct
Key
Key holds signing key material.
pkg/auth/token.go:89-95
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.
pkg/auth/token.go:98-103
type SignedToken struct
Fields
| Name | Type | Description |
|---|---|---|
| Token | string | |
| KeyID | string | |
| Payload | []byte | |
| Raw | string |
I
interface
TokenService
TokenService signs and verifies tokens.
pkg/auth/token.go:106-109
type TokenService interface
Methods
S
multiKeyService
pkg/auth/token.go:111-113
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 TokenService with one or more keys.
Parameters
keys
...Key
Returns
pkg/auth/token.go:116-118
func NewService(keys ...Key) TokenService
{
return &multiKeyService{keys: keys}
}
Uses
F
function
signWithKey
Returns
error
pkg/auth/token.go:141-175
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
return &SignedToken{
Token: token,
KeyID: key.ID,
Payload: data,
Raw: token,
}, nil
}
Uses
F
function
verifyWithKey
Parameters
token
string
key
Returns
T
error
pkg/auth/token.go:177-234
func verifyWithKey[T Claims](token string, key Key) (T, error)
{
var zero T
parts := splitToken(token)
if len(parts) != 3 {
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
pkg/auth/token.go:236-251
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)
}
return base64.RawURLEncoding.EncodeToString(data) + "." + base64.RawURLEncoding.EncodeToString(sig), nil
}
Uses
F
function
verify
Parameters
Returns
T
error
pkg/auth/token.go:253-301
func verify[T any](token string, secret []byte, alg Algorithm) (T, error)
{
var zero T
idx := -1
for i := 0; i < len(token); i++ {
if token[i] == '.' {
idx = i
break
}
}
if idx < 0 {
return zero, ErrInvalidToken
}
payloadBytes, err := base64.RawURLEncoding.DecodeString(token[:idx])
if err != nil {
return zero, ErrInvalidToken
}
sig, err := base64.RawURLEncoding.DecodeString(token[idx+1:])
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
pkg/auth/token.go:303-305
func signHMAC(data, secret []byte) ([]byte, error)
{
return computeSig(data, secret), nil
}
F
function
signRSA
Parameters
data
[]byte
key
Returns
[]byte
error
pkg/auth/token.go:307-310
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
pkg/auth/token.go:312-315
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
pkg/auth/token.go:317-319
func signEd25519(data []byte, key ed25519.PrivateKey) ([]byte, error)
{
return ed25519.Sign(key, data), nil
}
F
function
verifyRSA
Parameters
Returns
error
pkg/auth/token.go:321-324
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
pkg/auth/token.go:326-329
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
pkg/auth/token.go:331-333
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
pkg/auth/token.go:335-338
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
pkg/auth/token.go:340-344
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
[]string
pkg/auth/token.go:346-359
func splitToken(token string) []string
{
var parts []string
start := 0
for i := 0; i < len(token); i++ {
if token[i] == '.' {
parts = append(parts, token[start:i])
start = i + 1
}
}
if start < len(token) {
parts = append(parts, token[start:])
}
return parts
}
F
function
TestSignTokenAndVerifyToken
Parameters
t
pkg/auth/token_test.go:15-28
func TestSignTokenAndVerifyToken(t *testing.T)
{
secret := []byte("s3cr3t")
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
pkg/auth/token_test.go:30-35
func TestVerifyTokenRejectsMalformedToken(t *testing.T)
{
_, err := VerifyToken("invalid", []byte("s3cr3t"))
if !errors.Is(err, ErrInvalidToken) {
t.Fatalf("VerifyToken() error = %v, want ErrInvalidToken", err)
}
}
F
function
TestVerifyTokenRejectsInvalidSignature
Parameters
t
pkg/auth/token_test.go:37-52
func TestVerifyTokenRejectsInvalidSignature(t *testing.T)
{
secret := []byte("s3cr3t")
token, err := SignToken(Payload{Sub: "u1", Exp: time.Now().Add(time.Minute).Unix()}, secret)
if err != nil {
t.Fatalf("SignToken() error = %v", err)
}
parts := splitToken(token)
if len(parts) != 2 {
t.Fatal("expected 2 parts")
}
tampered := parts[0] + "." + 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
pkg/auth/token_test.go:54-64
func TestVerifyTokenRejectsExpiredToken(t *testing.T)
{
secret := []byte("s3cr3t")
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
pkg/auth/token_test.go:66-85
func TestNewService_KeyRotation(t *testing.T)
{
oldKey := Key{ID: "old", Secret: []byte("old-secret"), Algorithm: AlgHMACSHA256}
newKey := Key{ID: "new", Secret: []byte("new-secret"), Algorithm: AlgHMACSHA256}
svc := NewService(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
TestNewService_KeyRotationOldKeyStillWorks
Parameters
t
pkg/auth/token_test.go:87-101
func TestNewService_KeyRotationOldKeyStillWorks(t *testing.T)
{
oldKey := Key{ID: "old", Secret: []byte("old-secret"), Algorithm: AlgHMACSHA256}
newKey := Key{ID: "new", Secret: []byte("new-secret"), Algorithm: AlgHMACSHA256}
svcOld := NewService(oldKey)
token, _ := svcOld.Sign(&StandardClaims{Sub: "user1", Exp: time.Now().Add(time.Hour).Unix()})
svcNew := NewService(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
pkg/auth/token_test.go:103-113
func TestNewService_WrongKey(t *testing.T)
{
key1 := Key{ID: "k1", Secret: []byte("secret1"), Algorithm: AlgHMACSHA256}
key2 := Key{ID: "k2", Secret: []byte("secret2"), Algorithm: AlgHMACSHA256}
svc1 := NewService(key1)
token, _ := svc1.Sign(&StandardClaims{Sub: "u", Exp: time.Now().Add(time.Hour).Unix()})
svc2 := NewService(key2)
_, err := svc2.Verify(token.Token)
if err == nil {
t.Error("expected error when verifying with wrong key")
}
}
F
function
TestNewService_RS256
Parameters
t
pkg/auth/token_test.go:115-130
func TestNewService_RS256(t *testing.T)
{
priv, _ := rsa.GenerateKey(rand.Reader, 2048)
svc := NewService(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
pkg/auth/token_test.go:132-150
func TestNewService_EdDSA(t *testing.T)
{
pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("GenerateKey failed: %v", err)
}
svc := NewService(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
pkg/auth/token_test.go:152-170
func TestNewService_ECDSA(t *testing.T)
{
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("GenerateKey failed: %v", err)
}
svc := NewService(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
pkg/auth/token_test.go:172-177
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
pkg/auth/token_test.go:179-184
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
pkg/auth/token_test.go:186-192
func TestNewService_NoKeys(t *testing.T)
{
svc := NewService()
_, err := svc.Sign(&StandardClaims{Sub: "u", Exp: 0})
if err == nil {
t.Error("expected error with no keys")
}
}