• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

m-lab / token-exchange / 16038684699

03 Jul 2025 12:22AM UTC coverage: 33.687% (-16.7%) from 50.397%
16038684699

Pull #6

github

robertodauria
Update go.mod/sum
Pull Request #6: WIP: Add client integration schema and datastore manager

0 of 125 new or added lines in 1 file covered. (0.0%)

5 existing lines in 2 files now uncovered.

127 of 377 relevant lines covered (33.69%)

0.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.73
/internal/auth/jwt.go
1
package auth
2

3
import (
4
        "encoding/json"
5
        "fmt"
6
        "os"
7
        "time"
8

9
        "github.com/go-jose/go-jose/v4"
10
        "github.com/go-jose/go-jose/v4/jwt"
11
        "github.com/google/uuid"
12
)
13

14
const Issuer = "token-exchange"
15

16
// DefaultAudience is the default audience for JWT tokens.
17
// TODO(rd): Support audience selection (via different URLs or query string)
18
var DefaultAudience = jwt.Audience{"autojoin"}
19

20
// JWTSigner is a JWT signer that can be used to sign JWT tokens.
21
type JWTSigner struct {
22
        signer    jose.Signer
23
        publicJWK jose.JSONWebKey
24
}
25

26
// Claims is a JWT claims set that extends jwt.Claims with an additional field
27
// for the Organization.
28
type Claims struct {
29
        jwt.Claims
30
        Organization string `json:"org"`
31
}
32

33
// NewJWTSigner loads a private key from a JWK file and prepares a signer.
34
func NewJWTSigner(keyPath string) (*JWTSigner, error) {
1✔
35
        // Read private key JWK file
1✔
36
        keyBytes, err := os.ReadFile(keyPath)
1✔
37
        if err != nil {
2✔
38
                return nil, fmt.Errorf("failed to read private key file %s: %w", keyPath, err)
1✔
39
        }
1✔
40

41
        // Unmarshal the raw JSON into a jose.JSONWebKey
42
        var privateJWK jose.JSONWebKey
1✔
43
        if err := json.Unmarshal(keyBytes, &privateJWK); err != nil {
2✔
44
                return nil, fmt.Errorf("failed to unmarshal JWK from %s: %w", keyPath, err)
1✔
45
        }
1✔
46

47
        // Check if the key is private (required for signing)
48
        if privateJWK.IsPublic() {
2✔
49
                return nil, fmt.Errorf("JWK in %s is not a private key", keyPath)
1✔
50
        }
1✔
51

52
        // Ensure the key has a Key ID (kid)
53
        if privateJWK.KeyID == "" {
2✔
54
                return nil, fmt.Errorf("JWK in %s must have a 'kid' (Key ID)", keyPath)
1✔
55
        }
1✔
56

57
        // Create the signer using the private key
58
        signerOpts := (&jose.SignerOptions{}).WithHeader(jose.HeaderKey("kid"), privateJWK.KeyID)
1✔
59
        alg := jose.SignatureAlgorithm(privateJWK.Algorithm)
1✔
60
        signer, err := jose.NewSigner(jose.SigningKey{
1✔
61
                Algorithm: alg,
1✔
62
                Key:       privateJWK.Key,
1✔
63
        }, signerOpts)
1✔
64
        if err != nil {
1✔
UNCOV
65
                return nil, fmt.Errorf("failed to create jose signer: %w", err)
×
UNCOV
66
        }
×
67

68
        // Store the public part of the key for the JWKS endpoint
69
        publicJWK := privateJWK.Public()
1✔
70

1✔
71
        return &JWTSigner{
1✔
72
                signer:    signer,
1✔
73
                publicJWK: publicJWK,
1✔
74
        }, nil
1✔
75
}
76

77
// GenerateToken generates a JWT token for the given organization.
78
func (s *JWTSigner) GenerateToken(org string) (string, error) {
1✔
79
        now := time.Now()
1✔
80
        expiry := now.Add(time.Hour) // Token expiry: 1 hour
1✔
81

1✔
82
        claims := Claims{
1✔
83
                Organization: org,
1✔
84
                Claims: jwt.Claims{
1✔
85
                        ID:        uuid.New().String(),
1✔
86
                        Issuer:    Issuer,
1✔
87
                        IssuedAt:  jwt.NewNumericDate(now),
1✔
88
                        NotBefore: jwt.NewNumericDate(now),
1✔
89
                        Expiry:    jwt.NewNumericDate(expiry),
1✔
90
                        Audience:  DefaultAudience,
1✔
91
                },
1✔
92
        }
1✔
93

1✔
94
        signedToken, err := jwt.Signed(s.signer).Claims(claims).Serialize()
1✔
95
        if err != nil {
1✔
UNCOV
96
                return "", fmt.Errorf("failed to sign claims: %w", err)
×
UNCOV
97
        }
×
98

99
        return signedToken, nil
1✔
100
}
101

102
// GetPublicJWK returns the public key in jose.JSONWebKey format.
103
func (s *JWTSigner) GetPublicJWK() jose.JSONWebKey {
1✔
104
        return s.publicJWK
1✔
105
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc