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

iOS Swift应用通过Refresh Token获取Google Access Token的REST API方案

我之前在iOS Swift项目里正好实现过用Google Refresh Token静默刷新AccessToken,不用跳登录页,给你一步步讲清楚,包括REST端点细节和完整代码:

先确认核心前提
  • 你已经集成Google SignIn SDK,那必须确保登录时请求了offline_access权限,否则Google不会返回Refresh Token。
  • 第一次登录(或用户重新授权)时才能拿到Refresh Token,后续静默登录如果没触发授权弹窗,可能拿不到,所以必要时可以在登录请求里加prompt: "consent"强制弹出授权页,确保获取到Refresh Token。
  • 把Refresh Token存在Keychain里(别用UserDefaults,不安全),后续刷新时从Keychain读取。
iOS端可用的REST API端点细节

Google的Token刷新端点是固定的,不需要额外配置,直接调用即可:

  • 请求URL:https://oauth2.googleapis.com/token
  • 请求方法:POST
  • Content-Type:application/x-www-form-urlencoded
  • 必填参数:
    • grant_type: 固定值refresh_token
    • client_id: 你的iOS应用对应的OAuth 2.0客户端ID(从Google Cloud控制台的「凭据」里找类型为「iOS」的那个ID)
    • refresh_token: 你之前保存的有效Refresh Token

    注意:iOS客户端不需要传client_secret,Google的移动应用OAuth客户端没有这个字段。

完整Swift实现步骤

1. 确保获取并安全存储Refresh Token

登录成功后,从GIDAuthentication中提取Refresh Token,并存入Keychain:

import GoogleSignIn
import Security

// 登录请求时要包含offline_access scope
let signInConfig = GIDConfiguration(clientID: "你的iOS客户端ID")
signInConfig.additionalScopes = ["https://www.googleapis.com/auth/cloud-platform", "offline_access"]

GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { user, error in
    guard let user = user, let auth = user.authentication else {
        // 处理登录错误
        return
    }
    
    // 获取Refresh Token
    if let refreshToken = auth.refreshToken {
        // 存入Keychain(示例用原生Security框架,也可以用第三方Keychain库)
        let query: [CFString: Any] = [
            kSecClass: kSecClassGenericPassword,
            kSecAttrAccount: "GoogleRefreshToken",
            kSecValueData: refreshToken.data(using: .utf8)!,
            kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
        ]
        
        SecItemDelete(query as CFDictionary) // 先删除旧的
        let status = SecItemAdd(query as CFDictionary, nil)
        if status != errSecSuccess {
            print("存储Refresh Token失败")
        }
    }
}

2. 实现静默刷新AccessToken的方法

用URLSession发送POST请求到Google的Token端点,解析响应获取新的AccessToken:

func refreshGoogleAccessToken(completion: @escaping (Result<String, Error>) -> Void) {
    // 先从Keychain读取Refresh Token
    guard let refreshToken = getRefreshTokenFromKeychain() else {
        completion(.failure(NSError(domain: "RefreshTokenError", code: -1, userInfo: [NSLocalizedDescriptionKey: "未找到Refresh Token"])))
        return
    }
    
    let clientID = "你的iOS客户端ID"
    let url = URL(string: "https://oauth2.googleapis.com/token")!
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
    
    // 构造请求参数
    let parameters = [
        "grant_type": "refresh_token",
        "client_id": clientID,
        "refresh_token": refreshToken
    ]
    
    // 编码为url-encoded格式
    let parameterString = parameters.map { key, value in
        "\(key)=\(value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!)"
    }.joined(separator: "&")
    request.httpBody = parameterString.data(using: .utf8)
    
    // 发送请求
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        DispatchQueue.main.async {
            if let error = error {
                completion(.failure(error))
                return
            }
            
            guard let data = data else {
                completion(.failure(NSError(domain: "RefreshTokenError", code: -2, userInfo: [NSLocalizedDescriptionKey: "未收到响应数据"])))
                return
            }
            
            do {
                guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
                    completion(.failure(NSError(domain: "RefreshTokenError", code: -3, userInfo: [NSLocalizedDescriptionKey: "响应解析失败"])))
                    return
                }
                
                // 提取新的AccessToken
                guard let accessToken = json["access_token"] as? String else {
                    // 如果这里失败,大概率是Refresh Token失效,需要跳转登录
                    completion(.failure(NSError(domain: "RefreshTokenError", code: -4, userInfo: [NSLocalizedDescriptionKey: "Refresh Token已失效"])))
                    return
                }
                
                // 可选:保存AccessToken的过期时间(json里的expires_in是秒数)
                if let expiresIn = json["expires_in"] as? TimeInterval {
                    let expirationDate = Date().addingTimeInterval(expiresIn)
                    // 可以把过期时间也存在Keychain,下次提前刷新
                }
                
                completion(.success(accessToken))
            } catch {
                completion(.failure(error))
            }
        }
    }
    task.resume()
}

// 从Keychain读取Refresh Token的辅助方法
private func getRefreshTokenFromKeychain() -> String? {
    let query: [CFString: Any] = [
        kSecClass: kSecClassGenericPassword,
        kSecAttrAccount: "GoogleRefreshToken",
        kSecReturnData: kCFBooleanTrue!,
        kSecMatchLimit: kSecMatchLimitOne
    ]
    
    var data: AnyObject?
    let status = SecItemCopyMatching(query as CFDictionary, &data)
    
    if status == errSecSuccess, let tokenData = data as? Data {
        return String(data: tokenData, encoding: .utf8)
    }
    return nil
}

3. 使用刷新后的AccessToken

拿到新的AccessToken后,你可以:

  • 更新Google SignIn的当前用户认证信息,让SDK自动处理后续API调用:
refreshGoogleAccessToken { result in
    switch result {
    case .success(let accessToken):
        if let currentUser = GIDSignIn.sharedInstance.currentUser {
            let auth = GIDAuthentication(accessToken: accessToken, refreshToken: getRefreshTokenFromKeychain()!)
            currentUser.authentication = auth
            // 现在可以正常调用Google Cloud Prediction API了
        }
    case .failure(let error):
        // 刷新失败,比如Refresh Token失效,跳转到登录页
        GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self)
    }
}
  • 或者直接用这个AccessToken调用Prediction API,在请求头里加Authorization: Bearer \(accessToken)
关键注意事项
  • Refresh Token失效场景:如果用户撤销了应用权限、超过6个月未使用该Refresh Token、用户更改了Google账号密码,Refresh Token会失效,此时必须跳转登录页重新获取。
  • scope配置:一定要包含offline_access,否则Google不会返回Refresh Token。
  • 安全存储:Refresh Token是敏感信息,必须存在Keychain,绝对不能存在UserDefaults或明文存储。
  • 客户端ID正确性:必须使用iOS类型的OAuth客户端ID,不要混用Web/服务器端的ID,否则会请求失败。

内容的提问来源于stack exchange,提问作者vishnu venugopal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:23:03