Firebase OIDC认证Swift iOS:如何修改默认请求权限范围?
解决方案
由于Firebase OAuthProvider会强制追加email和profile scope,无法通过官方API移除,你可以绕过Firebase的内置OAuth流程,手动实现OIDC授权后再对接Firebase认证,具体步骤如下:
1. 手动构造IServ授权URL
使用OIDC标准参数构造授权请求,仅包含你需要的roles scope:
let clientID = "你的IServ客户端ID" let redirectURI = "你的回调URL(需在IServ和Firebase控制台配置)" let scope = "roles" let state = UUID().uuidString // 用于防止CSRF攻击 let codeChallenge = "生成的PKCE code challenge" // 推荐使用PKCE提升安全性 let codeChallengeMethod = "S256" guard let authURL = URL(string: "https://你的IServ域名/oauth/authorize") else { return } var components = URLComponents(url: authURL, resolvingAgainstBaseURL: false)! components.queryItems = [ URLQueryItem(name: "client_id", value: clientID), URLQueryItem(name: "redirect_uri", value: redirectURI), URLQueryItem(name: "response_type", value: "code"), URLQueryItem(name: "scope", value: scope), URLQueryItem(name: "state", value: state), URLQueryItem(name: "code_challenge", value: codeChallenge), URLQueryItem(name: "code_challenge_method", value: codeChallengeMethod) ] guard let finalAuthURL = components.url else { return }
2. 用WebView打开授权URL并处理回调
使用SFSafariViewController或WKWebView打开上述URL,监听回调URL获取授权码:
import SafariServices let safariVC = SFSafariViewController(url: finalAuthURL) safariVC.delegate = self present(safariVC, animated: true) // 在SFSafariViewControllerDelegate中处理关闭事件 func safariViewControllerDidFinish(_ controller: SFSafariViewController) { dismiss(animated: true) } // 通过AppDelegate/SceneDelegate处理URL回调 func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { guard url.scheme == "你的自定义URL Scheme" else { return false } guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false), let code = components.queryItems?.first(where: { $0.name == "code" })?.value, let state = components.queryItems?.first(where: { $0.name == "state" })?.value else { return false } // 验证state与之前生成的一致,防止CSRF攻击 if state == storedState { fetchToken(with: code) } dismiss(animated: true) return true }
3. 向IServ请求Token
用授权码向IServ的token endpoint换取id_token和access_token:
func fetchToken(with code: String) { let tokenURL = URL(string: "https://你的IServ域名/oauth/token")! var request = URLRequest(url: tokenURL) request.httpMethod = "POST" let parameters = [ "client_id": clientID, "redirect_uri": redirectURI, "grant_type": "authorization_code", "code": code, "code_verifier": "对应的PKCE code verifier" // 与之前的code challenge配对 ] request.httpBody = parameters.map { "\($0.key)=\($0.value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!)" }.joined(separator: "&").data(using: .utf8) request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") URLSession.shared.dataTask(with: request) { data, response, error in guard let data = data, error == nil else { return } do { let tokenResponse = try JSONDecoder().decode(TokenResponse.self, from: data) // 用获取到的token对接Firebase self.signInWithFirebase(idToken: tokenResponse.id_token, accessToken: tokenResponse.access_token) } catch { print("解析token失败:\(error)") } }.resume() } // 定义TokenResponse模型 struct TokenResponse: Codable { let id_token: String let access_token: String let expires_in: Int let token_type: String }
4. 使用Token完成Firebase认证
将IServ返回的id_token和access_token封装成Firebase的OAuthCredential,完成登录:
func signInWithFirebase(idToken: String, accessToken: String) { let credential = OAuthProvider.credential(withProviderID: "oidc.iserv", idToken: idToken, accessToken: accessToken) Auth.auth().signIn(with: credential) { authResult, error in if let error = error { print("Firebase认证失败:\(error.localizedDescription)") return } // 认证成功,处理用户信息 let user = authResult?.user print("登录成功:\(user?.uid ?? "")") } }
注意事项
- 确保在IServ管理后台和Firebase控制台正确配置回调URL和客户端ID。
- 推荐使用PKCE流程(code challenge/verifier),避免在客户端存储client_secret,提升安全性。
- 如果IServ要求客户端认证(需要client_secret),建议通过你的后端服务代理token请求,不要在iOS客户端中暴露client_secret。
内容的提问来源于stack exchange,提问作者Oscar Junius
相关产品推荐
相关产品推荐

