You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:39:38