-
Notifications
You must be signed in to change notification settings - Fork 40
/
session.go
123 lines (106 loc) · 3.37 KB
/
session.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package mockoidc
import (
"errors"
"strings"
"time"
"github.com/golang-jwt/jwt/v5"
)
// Session stores a User and their OIDC options across requests
type Session struct {
SessionID string
Scopes []string
OIDCNonce string
User User
Granted bool
CodeChallenge string
CodeChallengeMethod string
}
// SessionStore manages our Session objects
type SessionStore struct {
Store map[string]*Session
CodeQueue *CodeQueue
}
// IDTokenClaims are the mandatory claims any User.Claims implementation
// should use in their jwt.Claims building.
type IDTokenClaims struct {
Nonce string `json:"nonce,omitempty"`
*jwt.RegisteredClaims
}
// NewSessionStore initializes the SessionStore for this server
func NewSessionStore() *SessionStore {
return &SessionStore{
Store: make(map[string]*Session),
CodeQueue: &CodeQueue{},
}
}
// NewSession creates a new Session for a User
func (ss *SessionStore) NewSession(scope string, nonce string, user User, codeChallenge string, codeChallengeMethod string) (*Session, error) {
sessionID, err := ss.CodeQueue.Pop()
if err != nil {
return nil, err
}
session := &Session{
SessionID: sessionID,
Scopes: strings.Split(scope, " "),
OIDCNonce: nonce,
User: user,
CodeChallenge: codeChallenge,
CodeChallengeMethod: codeChallengeMethod,
}
ss.Store[sessionID] = session
return session, nil
}
// GetSessionByID looks up the Session
func (ss *SessionStore) GetSessionByID(id string) (*Session, error) {
session, ok := ss.Store[id]
if !ok {
return nil, errors.New("session not found")
}
return session, nil
}
// GetSessionByToken decodes a token and looks up a Session based on the
// session ID claim.
func (ss *SessionStore) GetSessionByToken(token *jwt.Token) (*Session, error) {
claims, ok := token.Claims.(jwt.MapClaims)
if !ok || !token.Valid {
return nil, errors.New("invalid token")
}
sessionID := claims["jti"].(string)
return ss.GetSessionByID(sessionID)
}
// AccessToken returns the JWT token with the appropriate claims for
// an access token
func (s *Session) AccessToken(config *Config, kp *Keypair, now time.Time) (string, error) {
claims := s.registeredClaims(config, config.AccessTTL, now)
return kp.SignJWT(claims)
}
// RefreshToken returns the JWT token with the appropriate claims for
// a refresh token
func (s *Session) RefreshToken(config *Config, kp *Keypair, now time.Time) (string, error) {
claims := s.registeredClaims(config, config.RefreshTTL, now)
return kp.SignJWT(claims)
}
// IDToken returns the JWT token with the appropriate claims for a user
// based on the scopes set.
func (s *Session) IDToken(config *Config, kp *Keypair, now time.Time) (string, error) {
base := &IDTokenClaims{
RegisteredClaims: s.registeredClaims(config, config.AccessTTL, now),
Nonce: s.OIDCNonce,
}
claims, err := s.User.Claims(s.Scopes, base)
if err != nil {
return "", err
}
return kp.SignJWT(claims)
}
func (s *Session) registeredClaims(config *Config, ttl time.Duration, now time.Time) *jwt.RegisteredClaims {
return &jwt.RegisteredClaims{
Audience: jwt.ClaimStrings{config.ClientID},
ExpiresAt: jwt.NewNumericDate(now.Add(ttl)),
ID: s.SessionID,
IssuedAt: jwt.NewNumericDate(now),
Issuer: config.Issuer,
NotBefore: jwt.NewNumericDate(now),
Subject: s.User.ID(),
}
}