From 2f53be7c0dec0e76a2752fc30ac90d9cef766cc0 Mon Sep 17 00:00:00 2001 From: zarzet Date: Thu, 9 Jul 2026 19:46:04 +0700 Subject: [PATCH] fix(extensions): expire stale pending auth challenges A pending verification challenge lived in a process-global map with no timestamp, so a challenge created while the app was backgrounded (and never completed) was served again verbatim on the next attempt - sending the user to an already-expired CAPTCHA page that needed a second request. Stamp PendingAuthRequest with CreatedAt at every writer and make ensureExtensionPendingAuthRequest discard entries older than 5 minutes, starting a fresh signed-session verification instead. --- go_backend/exports_extensions.go | 8 +++++++- go_backend/extension_runtime.go | 5 +++++ go_backend/extension_runtime_auth.go | 2 ++ go_backend/extension_signed_session.go | 1 + 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/go_backend/exports_extensions.go b/go_backend/exports_extensions.go index b8414d66..c0384b32 100644 --- a/go_backend/exports_extensions.go +++ b/go_backend/exports_extensions.go @@ -597,7 +597,13 @@ func ensureExtensionPendingAuthRequest(extensionID string) *PendingAuthRequest { } if req := GetPendingAuthRequest(extensionID); req != nil { - return req + if time.Since(req.CreatedAt) < pendingAuthRequestTTL { + return req + } + // The cached challenge is stale (e.g. verification was requested + // while the app was backgrounded and never completed); serving it + // would send the user to an expired page. Start a fresh one. + ClearPendingAuthRequest(extensionID) } manager := getExtensionManager() diff --git a/go_backend/extension_runtime.go b/go_backend/extension_runtime.go index ba968ff1..3f3dd7ac 100644 --- a/go_backend/extension_runtime.go +++ b/go_backend/extension_runtime.go @@ -59,8 +59,13 @@ type PendingAuthRequest struct { ExtensionID string AuthURL string CallbackURL string + CreatedAt time.Time } +// Challenge URLs are short-lived; serving one past this age sends the user +// to an already-expired verification page. +const pendingAuthRequestTTL = 5 * time.Minute + var ( pendingAuthRequests = make(map[string]*PendingAuthRequest) pendingAuthRequestsMu sync.RWMutex diff --git a/go_backend/extension_runtime_auth.go b/go_backend/extension_runtime_auth.go index 0622ecff..adc9ea54 100644 --- a/go_backend/extension_runtime_auth.go +++ b/go_backend/extension_runtime_auth.go @@ -78,6 +78,7 @@ func (r *extensionRuntime) authOpenUrl(call goja.FunctionCall) goja.Value { ExtensionID: r.extensionID, AuthURL: authURL, CallbackURL: callbackURL, + CreatedAt: time.Now(), } pendingAuthRequestsMu.Unlock() @@ -369,6 +370,7 @@ func (r *extensionRuntime) authStartOAuthWithPKCE(call goja.FunctionCall) goja.V ExtensionID: r.extensionID, AuthURL: fullAuthURL, CallbackURL: redirectURI, + CreatedAt: time.Now(), } pendingAuthRequestsMu.Unlock() diff --git a/go_backend/extension_signed_session.go b/go_backend/extension_signed_session.go index e750d8cc..0b445758 100644 --- a/go_backend/extension_signed_session.go +++ b/go_backend/extension_signed_session.go @@ -494,6 +494,7 @@ func (r *extensionRuntime) startSignedSessionVerification(config SignedSessionCo ExtensionID: r.extensionID, AuthURL: authURL, CallbackURL: config.CallbackURL, + CreatedAt: time.Now(), } pendingAuthRequestsMu.Unlock() }