使用SFAuthenticationSession实现Microsoft Graph用户登录及iOS11.3适配问题
我之前也踩过这个坑!iOS 11.3之后苹果对SafariViewController在SSO场景下的限制确实会导致这种无报错的崩溃,换成专门的SFAuthenticationSession(iOS12+之后是ASWebAuthenticationSession)就能解决问题。
问题根源
iOS 11.3开始,Apple不再允许用SafariViewController处理单点登录(SSO)流程——它无法正确共享Safari的登录状态,还会触发隐私相关的底层崩溃(无报错日志就是因为这个原因)。官方明确要求SSO场景必须使用专门的SFAuthenticationSession或其后续替代类。
解决方案:基于p2_oauth2适配SFAuthenticationSession
p2_oauth2库支持自定义浏览器实现,我们可以通过扩展OAuth2Browser协议,用官方推荐的认证会话类替换掉原来的SafariViewController。
第一步:创建自定义浏览器类
import UIKit import SafariServices import p2_OAuth2 class OAuth2AuthSessionBrowser: NSObject, OAuth2Browser { weak var presentingVC: UIViewController? private var authSession: SFAuthenticationSession? func present(_ url: URL, context: Any?, animated: Bool, completion: @escaping (Bool) -> Void) { guard let vc = presentingVC else { completion(false) return } // 区分iOS版本使用对应认证会话 if #available(iOS 12.0, *) { let session = ASWebAuthenticationSession(url: url, callbackURLScheme: oauth2.redirectURI?.scheme) { [weak self] callbackURL, error in self?.handleAuthCallback(url: callbackURL, error: error) } session.presentationContextProvider = self self.authSession = session session.start() } else { let session = SFAuthenticationSession(url: url, callbackURLScheme: oauth2.redirectURI?.scheme) { [weak self] callbackURL, error in self?.handleAuthCallback(url: callbackURL, error: error) } self.authSession = session session.start() } completion(true) } func dismiss(animated: Bool, completion: @escaping () -> Void) { authSession?.cancel() authSession = nil completion() } private func handleAuthCallback(url: URL?, error: Error?) { if let callbackURL = url { oauth2.handleRedirectURL(callbackURL) } else if let authError = error { oauth2.abortAuthorization(error: authError) } } } // 适配iOS12+的上下文提供协议 @available(iOS 12.0, *) extension OAuth2AuthSessionBrowser: ASWebAuthenticationPresentationContextProviding { func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { return presentingVC?.view.window ?? UIApplication.shared.windows.first! } }
第二步:配置OAuth2实例使用自定义浏览器
// 初始化OAuth2实例并绑定自定义浏览器 static var oauth2: OAuth2CodeGrant = { let authSettings = [ "client_id": "myClientID", "authorize_uri": "https://myauthserver.com/auth", "token_uri": "https://myauthserver.com/token", "redirect_uri": "myapp://oauth/callback", "scope": "read write", ] let oauth = OAuth2CodeGrant(settings: authSettings) // 配置自定义浏览器 let customBrowser = OAuth2AuthSessionBrowser() customBrowser.presentingVC = UIApplication.shared.keyWindow?.rootViewController oauth.browser = customBrowser return oauth }() // 发起登录的调用方式 func startOAuthLogin() { oauth2.authorize { result in switch result { case .success(let token): print("登录成功,Token: \(token)") // 处理登录后的业务逻辑 case .failure(let error): print("登录失败: \(error.localizedDescription)") } } }
额外注意事项
- 确保
redirect_uri对应的URL Scheme已经在Xcode的Info.plist中配置(添加CFBundleURLSchemes数组,包含你的scheme值)。 - iOS13+需要在
Info.plist中添加NSUserActivityTypes,包含与回调URL对应的类型(比如$(PRODUCT_BUNDLE_IDENTIFIER).oauth-callback)。 - 如果需要兼容iOS11以下版本,可以保留原SafariViewController的逻辑做版本判断。
内容的提问来源于stack exchange,提问作者Jacob Jidell
相关产品推荐
相关产品推荐

