import Foundation enum RedditWebAuthHelper { static let redditHomeURL = URL(string: "https://www.reddit.com")! private static let oauthHosts = [ "accounts.google.com", "appleid.apple.com", "idmsa.apple.com", "gsa.apple.com", "login.microsoftonline.com", "login.live.com", "account.live.com", "login.microsoft.com", "www.facebook.com", "m.facebook.com", "facebook.com", "auth0.com", ] private static let authFlowPathPrefixes = [ "/login", "/register", "/password", "/account/login", "/account/register", "/account/signup", ] static func isOAuthProviderURL(_ url: URL) -> Bool { guard let host = url.host?.lowercased() else { return false } if oauthHosts.contains(where: { host == $0 || host.hasSuffix(".\($0)") }) { return true } if host == "google.com" || host.hasSuffix(".google.com") { let path = url.path.lowercased() return path.contains("oauth") || path.contains("signin") || path.contains("gsi") } if host == "apple.com" || host.hasSuffix(".apple.com") { let path = url.path.lowercased() return path.contains("auth") || path.contains("signin") || path.contains("sso") } if host == "facebook.com" || host.hasSuffix(".facebook.com") { let path = url.path.lowercased() return path.contains("oauth") || path.contains("login") || path.contains("dialog") } if host.hasSuffix(".auth0.com") { return true } return false } static func isRedditHomeURL(_ url: URL) -> Bool { guard isRedditURL(url) else { return false } let host = url.host?.lowercased() ?? "" let isMainSite = host == "reddit.com" || host == "www.reddit.com" || host == "new.reddit.com" || host == "old.reddit.com" guard isMainSite else { return false } let path = url.path.lowercased() return path.isEmpty || path == "/" || path == "/home" } static func isRedditURL(_ url: URL) -> Bool { guard let host = url.host?.lowercased() else { return false } let redditHosts = [ "reddit.com", "www.reddit.com", "old.reddit.com", "new.reddit.com", "accounts.reddit.com", "oauth.reddit.com", "reddit.app.link", "redd.it", ] return redditHosts.contains(where: { host == $0 || host.hasSuffix(".\($0)") }) } static func isAuthFlowURL(_ url: URL) -> Bool { guard isRedditURL(url) else { return false } let path = url.path.lowercased() return authFlowPathPrefixes.contains { prefix in path == prefix || path.hasPrefix("\(prefix)/") } } static func looksLikeLoggedInURL(_ url: URL) -> Bool { guard isRedditURL(url) else { return false } if isAuthFlowURL(url) { return false } let path = url.path.lowercased() if url.host?.lowercased() == "accounts.reddit.com" { if path.contains("/oauth") || path.contains("/authorize") || path.contains("/login") { return false } return path.isEmpty || path == "/" || path.hasPrefix("/api/") } return true } /// Returns true when the OAuth completion panel can close and hand off to the main WebView. static func shouldCompleteOAuthSignIn(for url: URL) -> Bool { guard isRedditURL(url) else { return false } if isAuthFlowURL(url) { return false } let host = url.host?.lowercased() ?? "" let path = url.path.lowercased() if host == "accounts.reddit.com" { if path.contains("/oauth") || path.contains("/authorize") || path.contains("/login") || path.hasPrefix("/api/") { return false } return path.isEmpty || path == "/" } if host == "www.reddit.com" || host == "reddit.com" || host == "new.reddit.com" || host == "old.reddit.com" { return true } return looksLikeLoggedInURL(url) } static func oauthCallbackHost(for url: URL) -> String { if let host = parsedRedirectHost(from: url) { return host } return "accounts.reddit.com" } private static func parsedRedirectHost(from url: URL) -> String? { guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return nil } let redirectValue = components.queryItems?.first(where: { $0.name == "redirect_uri" || $0.name == "redirect_url" || $0.name == "redirect" })?.value guard let redirectValue else { return nil } let decoded = redirectValue.removingPercentEncoding ?? redirectValue if let host = hostFromRedirectString(decoded) { return host } if !decoded.contains("://"), let host = hostFromRedirectString("https://\(decoded)") { return host } return nil } private static func hostFromRedirectString(_ value: String) -> String? { guard let redirectURL = URL(string: value), let host = redirectURL.host?.lowercased(), isRedditURL(redirectURL) else { return nil } return host } }