···7474 m.lk.Lock()
7575 defer m.lk.Unlock()
76767777+ if _, ok := m.requests[info.State]; ok {
7878+ // Should be unreachable, barring implementation bugs elsewhere
7979+ return fmt.Errorf("auth request already saved for state %s", info.State)
8080+ }
8181+7782 m.requests[info.State] = info
7883 return nil
7984}
+5-1
atproto/auth/oauth/store.go
···1010//
1111// This interface supports multiple sessions for a single account (DID). This is helpful for traditional web app backends where a single user might log in and have concurrent sessions from multiple browsers/devices. For situations where multiple sessions are not required, implementations of this interface could ignore the `sessionID` parameters, though this could result in clobbering of active sessions.
1212//
1313-// For authorization-only (authn-only) applications, the `SaveSession()` method could be a no-op.
1313+// For authentication-only (authn-only) applications, the `SaveSession()` method could be a no-op.
1414//
1515// Implementations should generally allow for concurrent access.
1616+//
1717+// `SaveSession()` should be treated as an "upsert" operation (updating a previously saved session with matching did and sessionID, if present). `SaveAuthRequestInfo()` however is create-only.
1818+//
1919+// Implementations are responsible for garbage-collecting expired sessions and auth requests.
1620type ClientAuthStore interface {
1721 GetSession(ctx context.Context, did syntax.DID, sessionID string) (*ClientSessionData, error)
1822 SaveSession(ctx context.Context, sess ClientSessionData) error