fix(oauth): make session completion idempotent

This commit is contained in:
Supra4E8C
2026-07-10 16:20:15 +08:00
parent d1ef06cb5e
commit 7115e7e00c
2 changed files with 49 additions and 2 deletions
@@ -155,7 +155,7 @@ func (s *oauthSessionStore) Complete(state string) {
s.purgeExpiredLocked(now)
session, ok := s.sessions[state]
if !ok {
if !ok || session.Completed {
return
}
session.Status = ""
@@ -179,7 +179,7 @@ func (s *oauthSessionStore) CompleteProvider(provider string, source string) int
s.purgeExpiredLocked(now)
removed := 0
for state, session := range s.sessions {
if strings.EqualFold(session.Provider, provider) && (source == "" || session.Source == source) {
if !session.Completed && strings.EqualFold(session.Provider, provider) && (source == "" || session.Source == source) {
session.Status = ""
session.Metadata = nil
session.Completed = true
@@ -26,6 +26,53 @@ func TestOAuthSessionStoreCompleteKeepsShortLivedSession(t *testing.T) {
}
}
func TestOAuthSessionStoreCompleteDoesNotExtendCompletedSession(t *testing.T) {
store := newOAuthSessionStore(time.Minute)
store.Register("completed-state", "codex")
store.Complete("completed-state")
before, ok := store.Get("completed-state")
if !ok {
t.Fatal("completed OAuth session tombstone is missing")
}
store.completedTTL = 2 * time.Minute
store.Complete("completed-state")
after, ok := store.Get("completed-state")
if !ok {
t.Fatal("completed OAuth session tombstone is missing after repeated completion")
}
if !after.ExpiresAt.Equal(before.ExpiresAt) {
t.Fatalf("repeated completion extended expiry from %s to %s", before.ExpiresAt, after.ExpiresAt)
}
}
func TestOAuthSessionStoreCompleteProviderSkipsCompletedSessions(t *testing.T) {
store := newOAuthSessionStore(time.Minute)
store.Register("completed-state", "codex")
store.Register("pending-state", "codex")
store.Complete("completed-state")
completedBefore, ok := store.Get("completed-state")
if !ok {
t.Fatal("completed OAuth session tombstone is missing")
}
store.completedTTL = 2 * time.Minute
if got := store.CompleteProvider("codex", oauthSessionSourceBuiltin); got != 1 {
t.Fatalf("CompleteProvider() = %d, want 1 newly completed session", got)
}
completedAfter, ok := store.Get("completed-state")
if !ok {
t.Fatal("completed OAuth session tombstone is missing after provider completion")
}
if !completedAfter.ExpiresAt.Equal(completedBefore.ExpiresAt) {
t.Fatalf("provider completion extended existing tombstone from %s to %s", completedBefore.ExpiresAt, completedAfter.ExpiresAt)
}
pendingAfter, ok := store.Get("pending-state")
if !ok || !pendingAfter.Completed {
t.Fatalf("pending session completed/ok = %t/%t, want true/true", pendingAfter.Completed, ok)
}
}
func TestGetOAuthSessionHidesCompletedSession(t *testing.T) {
store := newOAuthSessionStore(time.Minute)
replaceOAuthSessionStoreForTest(t, store)