forked from oauth2-proxy/mockoidc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
session.go
129 lines (112 loc) · 3.36 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
124
125
126
127
128
129
package mockoidc
import (
"errors"
"strings"
"sync"
"time"
"github.com/golang-jwt/jwt"
)
// 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 {
sync.RWMutex
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.StandardClaims
}
// 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.Lock()
ss.Store[sessionID] = session
ss.Unlock()
return session, nil
}
// GetSessionByID looks up the Session
func (ss *SessionStore) GetSessionByID(id string) (*Session, error) {
ss.RLock()
session, ok := ss.Store[id]
ss.RUnlock()
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.standardClaims(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.standardClaims(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{
StandardClaims: s.standardClaims(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) standardClaims(config *Config, ttl time.Duration, now time.Time) *jwt.StandardClaims {
return &jwt.StandardClaims{
Audience: config.ClientID,
ExpiresAt: now.Add(ttl).Unix(),
Id: s.SessionID,
IssuedAt: now.Unix(),
Issuer: config.Issuer,
NotBefore: now.Unix(),
Subject: s.User.ID(),
}
}