···
9
+
"tangled.sh/tangled.sh/core/appview/cache"
12
+
type OAuthSession struct {
18
+
AuthServerIss string
20
+
DpopAuthserverNonce string
21
+
DpopPrivateJwk string
25
+
type OAuthRequest struct {
26
+
AuthserverIss string
32
+
DpopAuthserverNonce string
33
+
DpopPrivateJwk string
36
+
type SessionStore struct {
41
+
stateKey = "oauthstate:%s"
42
+
requestKey = "oauthrequest:%s"
43
+
sessionKey = "oauthsession:%s"
46
+
func New(cache *cache.Cache) *SessionStore {
47
+
return &SessionStore{cache: cache}
50
+
func (s *SessionStore) SaveSession(ctx context.Context, session OAuthSession) error {
51
+
key := fmt.Sprintf(sessionKey, session.Did)
52
+
data, err := json.Marshal(session)
57
+
// set with ttl (expires in + buffer)
58
+
expiry, _ := time.Parse(time.RFC3339, session.Expiry)
59
+
ttl := time.Until(expiry) + time.Minute
61
+
return s.cache.Set(ctx, key, data, ttl).Err()
64
+
// SaveRequest stores the OAuth request to be later fetched in the callback. Since
65
+
// the fetching happens by comparing the state we get in the callback params, we
66
+
// store an additional state->did mapping which then lets us fetch the whole OAuth request.
67
+
func (s *SessionStore) SaveRequest(ctx context.Context, request OAuthRequest) error {
68
+
key := fmt.Sprintf(requestKey, request.Did)
69
+
data, err := json.Marshal(request)
74
+
// oauth flow must complete within 30 minutes
75
+
err = s.cache.Set(ctx, key, data, 30*time.Minute).Err()
77
+
return fmt.Errorf("error saving request: %w", err)
80
+
stateKey := fmt.Sprintf(stateKey, request.State)
81
+
err = s.cache.Set(ctx, stateKey, request.Did, 30*time.Minute).Err()
83
+
return fmt.Errorf("error saving state->did mapping: %w", err)
89
+
func (s *SessionStore) GetSession(ctx context.Context, did string) (*OAuthSession, error) {
90
+
key := fmt.Sprintf(sessionKey, did)
91
+
val, err := s.cache.Get(ctx, key).Result()
96
+
var session OAuthSession
97
+
err = json.Unmarshal([]byte(val), &session)
101
+
return &session, nil
104
+
func (s *SessionStore) GetRequestByState(ctx context.Context, state string) (*OAuthRequest, error) {
105
+
didKey, err := s.getRequestKey(ctx, state)
110
+
val, err := s.cache.Get(ctx, didKey).Result()
115
+
var request OAuthRequest
116
+
err = json.Unmarshal([]byte(val), &request)
121
+
return &request, nil
124
+
func (s *SessionStore) DeleteSession(ctx context.Context, did string) error {
125
+
key := fmt.Sprintf(sessionKey, did)
126
+
return s.cache.Del(ctx, key).Err()
129
+
func (s *SessionStore) DeleteRequestByState(ctx context.Context, state string) error {
130
+
didKey, err := s.getRequestKey(ctx, state)
135
+
err = s.cache.Del(ctx, fmt.Sprintf(stateKey, "state")).Err()
140
+
return s.cache.Del(ctx, didKey).Err()
143
+
func (s *SessionStore) RefreshSession(ctx context.Context, did, access, refresh, expiry string) error {
144
+
session, err := s.GetSession(ctx, did)
148
+
session.AccessJwt = access
149
+
session.RefreshJwt = refresh
150
+
session.Expiry = expiry
151
+
return s.SaveSession(ctx, *session)
154
+
func (s *SessionStore) UpdateNonce(ctx context.Context, did, nonce string) error {
155
+
session, err := s.GetSession(ctx, did)
159
+
session.DpopAuthserverNonce = nonce
160
+
return s.SaveSession(ctx, *session)
163
+
func (s *SessionStore) getRequestKeyFromState(ctx context.Context, state string) (string, error) {
164
+
key := fmt.Sprintf(stateKey, state)
165
+
did, err := s.cache.Get(ctx, key).Result()
170
+
didKey := fmt.Sprintf(requestKey, did)