summaryrefslogtreecommitdiff
path: root/vendor/github.com/oauth2-proxy/mockoidc/session.go
blob: 4107a3868bca4c45afd74c2a1107db7d82111871 (plain)
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(),
	}
}