@@ -12,8 +12,18 @@ import (
1212type Manager struct {
1313 privateKey ed25519.PrivateKey
1414 publicKey ed25519.PublicKey
15+ issuer string
16+ clockSkew time.Duration
17+ now func () time.Time
1518}
1619
20+ type Option func (* Manager )
21+
22+ const (
23+ defaultIssuer = "asb"
24+ defaultClockSkew = 30 * time .Second
25+ )
26+
1727type claims struct {
1828 SessionID string `json:"sid"`
1929 TenantID string `json:"tenant_id"`
@@ -24,21 +34,56 @@ type claims struct {
2434 jwt.RegisteredClaims
2535}
2636
27- func NewManager (privateKey ed25519.PrivateKey ) (* Manager , error ) {
37+ func WithIssuer (issuer string ) Option {
38+ return func (manager * Manager ) {
39+ if issuer != "" {
40+ manager .issuer = issuer
41+ }
42+ }
43+ }
44+
45+ func WithClockSkew (clockSkew time.Duration ) Option {
46+ return func (manager * Manager ) {
47+ if clockSkew >= 0 {
48+ manager .clockSkew = clockSkew
49+ }
50+ }
51+ }
52+
53+ func WithNowFunc (now func () time.Time ) Option {
54+ return func (manager * Manager ) {
55+ if now != nil {
56+ manager .now = now
57+ }
58+ }
59+ }
60+
61+ func NewManager (privateKey ed25519.PrivateKey , options ... Option ) (* Manager , error ) {
2862 if len (privateKey ) == 0 {
2963 return nil , fmt .Errorf ("%w: private key is required" , core .ErrInvalidRequest )
3064 }
3165 publicKey , ok := privateKey .Public ().(ed25519.PublicKey )
3266 if ! ok {
3367 return nil , fmt .Errorf ("%w: private key public component is %T, want ed25519.PublicKey" , core .ErrInvalidRequest , privateKey .Public ())
3468 }
35- return & Manager {
69+ manager := & Manager {
3670 privateKey : privateKey ,
3771 publicKey : publicKey ,
38- }, nil
72+ issuer : defaultIssuer ,
73+ clockSkew : defaultClockSkew ,
74+ now : time .Now ,
75+ }
76+ for _ , option := range options {
77+ option (manager )
78+ }
79+ return manager , nil
3980}
4081
4182func (m * Manager ) Sign (session * core.Session ) (string , error ) {
83+ tokenID := session .TokenID
84+ if tokenID == "" {
85+ tokenID = session .ID
86+ }
4287 token := jwt .NewWithClaims (jwt .SigningMethodEdDSA , claims {
4388 SessionID : session .ID ,
4489 TenantID : session .TenantID ,
@@ -49,7 +94,10 @@ func (m *Manager) Sign(session *core.Session) (string, error) {
4994 RegisteredClaims : jwt.RegisteredClaims {
5095 ExpiresAt : jwt .NewNumericDate (session .ExpiresAt ),
5196 IssuedAt : jwt .NewNumericDate (session .CreatedAt ),
97+ NotBefore : jwt .NewNumericDate (session .CreatedAt .Add (- m .clockSkew )),
98+ Issuer : m .issuer ,
5299 Subject : session .WorkloadIdentity .Subject ,
100+ ID : tokenID ,
53101 },
54102 })
55103 return token .SignedString (m .privateKey )
@@ -61,7 +109,7 @@ func (m *Manager) Verify(raw string) (*core.SessionClaims, error) {
61109 return nil , fmt .Errorf ("%w: unexpected signing method %q" , core .ErrUnauthorized , token .Method .Alg ())
62110 }
63111 return m .publicKey , nil
64- })
112+ }, jwt . WithIssuer ( m . issuer ), jwt . WithIssuedAt (), jwt . WithLeeway ( m . clockSkew ), jwt . WithTimeFunc ( m . now ) )
65113 if err != nil {
66114 return nil , fmt .Errorf ("%w: %v" , core .ErrUnauthorized , err )
67115 }
@@ -70,21 +118,27 @@ func (m *Manager) Verify(raw string) (*core.SessionClaims, error) {
70118 if ! ok || ! parsed .Valid {
71119 return nil , fmt .Errorf ("%w: invalid session token" , core .ErrUnauthorized )
72120 }
121+ if tokenClaims .ExpiresAt == nil {
122+ return nil , fmt .Errorf ("%w: missing exp" , core .ErrUnauthorized )
123+ }
124+ if tokenClaims .NotBefore == nil {
125+ return nil , fmt .Errorf ("%w: missing nbf" , core .ErrUnauthorized )
126+ }
127+ if tokenClaims .ID == "" {
128+ return nil , fmt .Errorf ("%w: missing jti" , core .ErrUnauthorized )
129+ }
130+ if tokenClaims .SessionID == "" || tokenClaims .TenantID == "" {
131+ return nil , fmt .Errorf ("%w: missing required session claims" , core .ErrUnauthorized )
132+ }
73133
74134 return & core.SessionClaims {
75135 SessionID : tokenClaims .SessionID ,
76136 TenantID : tokenClaims .TenantID ,
77137 AgentID : tokenClaims .AgentID ,
78138 RunID : tokenClaims .RunID ,
139+ TokenID : tokenClaims .ID ,
79140 ToolContext : append ([]string (nil ), tokenClaims .ToolContext ... ),
80141 WorkloadHash : tokenClaims .WorkloadHash ,
81142 ExpiresAt : tokenClaims .ExpiresAt .Time ,
82143 }, nil
83144}
84-
85- func (c * claims ) Valid () error {
86- if c .ExpiresAt == nil {
87- return fmt .Errorf ("%w: missing exp" , core .ErrUnauthorized )
88- }
89- return jwt .NewValidator (jwt .WithTimeFunc (time .Now )).Validate (c .RegisteredClaims )
90- }
0 commit comments