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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 23:15:37