···
+
"tangled.sh/tangled.sh/core/appview/cache"
+
type OAuthSession struct {
+
DpopAuthserverNonce string
+
type OAuthRequest struct {
+
DpopAuthserverNonce string
+
type SessionStore struct {
+
stateKey = "oauthstate:%s"
+
requestKey = "oauthrequest:%s"
+
sessionKey = "oauthsession:%s"
+
func New(cache *cache.Cache) *SessionStore {
+
return &SessionStore{cache: cache}
+
func (s *SessionStore) SaveSession(ctx context.Context, session OAuthSession) error {
+
key := fmt.Sprintf(sessionKey, session.Did)
+
data, err := json.Marshal(session)
+
// set with ttl (expires in + buffer)
+
expiry, _ := time.Parse(time.RFC3339, session.Expiry)
+
ttl := time.Until(expiry) + time.Minute
+
return s.cache.Set(ctx, key, data, ttl).Err()
+
// SaveRequest stores the OAuth request to be later fetched in the callback. Since
+
// the fetching happens by comparing the state we get in the callback params, we
+
// store an additional state->did mapping which then lets us fetch the whole OAuth request.
+
func (s *SessionStore) SaveRequest(ctx context.Context, request OAuthRequest) error {
+
key := fmt.Sprintf(requestKey, request.Did)
+
data, err := json.Marshal(request)
+
// oauth flow must complete within 30 minutes
+
err = s.cache.Set(ctx, key, data, 30*time.Minute).Err()
+
return fmt.Errorf("error saving request: %w", err)
+
stateKey := fmt.Sprintf(stateKey, request.State)
+
err = s.cache.Set(ctx, stateKey, request.Did, 30*time.Minute).Err()
+
return fmt.Errorf("error saving state->did mapping: %w", err)
+
func (s *SessionStore) GetSession(ctx context.Context, did string) (*OAuthSession, error) {
+
key := fmt.Sprintf(sessionKey, did)
+
val, err := s.cache.Get(ctx, key).Result()
+
var session OAuthSession
+
err = json.Unmarshal([]byte(val), &session)
+
func (s *SessionStore) GetRequestByState(ctx context.Context, state string) (*OAuthRequest, error) {
+
didKey, err := s.getRequestKey(ctx, state)
+
val, err := s.cache.Get(ctx, didKey).Result()
+
var request OAuthRequest
+
err = json.Unmarshal([]byte(val), &request)
+
func (s *SessionStore) DeleteSession(ctx context.Context, did string) error {
+
key := fmt.Sprintf(sessionKey, did)
+
return s.cache.Del(ctx, key).Err()
+
func (s *SessionStore) DeleteRequestByState(ctx context.Context, state string) error {
+
didKey, err := s.getRequestKey(ctx, state)
+
err = s.cache.Del(ctx, fmt.Sprintf(stateKey, "state")).Err()
+
return s.cache.Del(ctx, didKey).Err()
+
func (s *SessionStore) RefreshSession(ctx context.Context, did, access, refresh, expiry string) error {
+
session, err := s.GetSession(ctx, did)
+
session.AccessJwt = access
+
session.RefreshJwt = refresh
+
session.Expiry = expiry
+
return s.SaveSession(ctx, *session)
+
func (s *SessionStore) UpdateNonce(ctx context.Context, did, nonce string) error {
+
session, err := s.GetSession(ctx, did)
+
session.DpopAuthserverNonce = nonce
+
return s.SaveSession(ctx, *session)
+
func (s *SessionStore) getRequestKeyFromState(ctx context.Context, state string) (string, error) {
+
key := fmt.Sprintf(stateKey, state)
+
did, err := s.cache.Get(ctx, key).Result()
+
didKey := fmt.Sprintf(requestKey, did)