@@ -261,6 +261,9 @@ public struct AmpUsageFetcher: Sendable {
261261 if httpResponse. statusCode == 401 || httpResponse. statusCode == 403 {
262262 throw AmpUsageError . invalidCredentials
263263 }
264+ if diagnostics. detectedLoginRedirect {
265+ throw AmpUsageError . invalidCredentials
266+ }
264267 throw AmpUsageError . networkError ( " HTTP \( httpResponse. statusCode) " )
265268 }
266269
@@ -277,6 +280,7 @@ public struct AmpUsageFetcher: Sendable {
277280 private let cookieHeader : String
278281 private let logger : ( ( String ) -> Void ) ?
279282 var redirects : [ String ] = [ ]
283+ private( set) var detectedLoginRedirect = false
280284
281285 init ( cookieHeader: String , logger: ( ( String ) -> Void ) ? ) {
282286 self . cookieHeader = cookieHeader
@@ -293,6 +297,16 @@ public struct AmpUsageFetcher: Sendable {
293297 let from = response. url? . absoluteString ?? " unknown "
294298 let to = request. url? . absoluteString ?? " unknown "
295299 self . redirects. append ( " \( response. statusCode) \( from) -> \( to) " )
300+
301+ if let toURL = request. url, AmpUsageFetcher . isLoginRedirect ( toURL) {
302+ if let logger {
303+ logger ( " [amp] Detected login redirect, aborting (invalid session) " )
304+ }
305+ self . detectedLoginRedirect = true
306+ completionHandler ( nil )
307+ return
308+ }
309+
296310 var updated = request
297311 if AmpUsageFetcher . shouldAttachCookie ( to: request. url) , !self . cookieHeader. isEmpty {
298312 updated. setValue ( self . cookieHeader, forHTTPHeaderField: " Cookie " )
@@ -364,4 +378,25 @@ public struct AmpUsageFetcher: Sendable {
364378 if host == " ampcode.com " || host == " www.ampcode.com " { return true }
365379 return host. hasSuffix ( " .ampcode.com " )
366380 }
381+
382+ static func isLoginRedirect( _ url: URL ) -> Bool {
383+ guard self . shouldAttachCookie ( to: url) else { return false }
384+
385+ let path = url. path. lowercased ( )
386+ let components = path. split ( separator: " / " ) . map ( String . init)
387+ if components. contains ( " login " ) { return true }
388+ if components. contains ( " signin " ) { return true }
389+ if components. contains ( " sign-in " ) { return true }
390+
391+ // Amp currently redirects to /auth/sign-in?returnTo=... when session is invalid. Keep this slightly broader
392+ // than one exact path so we keep working if Amp changes auth routes.
393+ if components. contains ( " auth " ) {
394+ let query = url. query? . lowercased ( ) ?? " "
395+ if query. contains ( " returnto= " ) { return true }
396+ if query. contains ( " redirect= " ) { return true }
397+ if query. contains ( " redirectto= " ) { return true }
398+ }
399+
400+ return false
401+ }
367402}
0 commit comments