swift - 用于向 Google API (Swift) 验证服务帐户的 JWT 签名无效
问题描述
我是 Google API 的新手;我正在尝试在没有用户交互的情况下检索服务帐户的访问令牌。我读过这样做的方法是生成一个 JWT,然后运行一个对 API 的请求,但是当我将 JWT 发送给 Google 时,我不断收到这个错误:
{"error":"invalid_grant","error_description":"Invalid JWT Signature."}
这是生成 JWT 的代码:
extension Data {
func urlSafeBase64EncodedString() -> String {
return base64EncodedString()
.replacingOccurrences(of: "+", with: "-")
.replacingOccurrences(of: "/", with: "_")
.replacingOccurrences(of: "=", with: "")
}
}
struct Header: Encodable {
let alg = "RS256"
let typ = "JWT"
}
struct Payload: Encodable {
let iss = "content-uploader@project-id.iam.gserviceaccount.com"
let scope = "https://www.googleapis.com/auth/devstorage.full_control"
let aud = "https://oauth2.googleapis.com/token"
let iat = Date().timeIntervalSince1970
let exp = Date().timeIntervalSince1970.advanced(by: 1000)
}
let secret = "-----BEGIN PRIVATE KEY-----HERE IS A TON OF NUMBERS, SLASHES, PLUS SIGNS, GOING ON FOR HUNDREDS OF CHARACTERS. THIS WAS TAKEN FROM THE KEY DOWNLOADED AS A JSON FROM THE CREDENTIALS PAGE FOR THE SERVICE ACCOUNT IN GOOGLE CLOUD.\n-----END PRIVATE KEY-----\n"
let privateKey = SymmetricKey(data: secret.data(using: .utf8)!)
let headerJSONData = try! JSONEncoder().encode(Header())
let headerBase64String = headerJSONData.urlSafeBase64EncodedString()
let payloadJSONData = try! JSONEncoder().encode(Payload())
let payloadBase64String = payloadJSONData.urlSafeBase64EncodedString()
let toSign = (headerBase64String + "." + payloadBase64String).data(using: .utf8)!
let signature = HMAC<SHA256>.authenticationCode(for: toSign, using: privateKey)
let signatureBase64String = Data(signature).urlSafeBase64EncodedString()
let token = [headerBase64String, payloadBase64String, signatureBase64String].joined(separator: ".")
以下是正在运行的请求:
if let url = URL(string: "https://oauth2.googleapis.com/token?grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=\(token)") {
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if data != nil {
print("Data: \(String(data: data!, encoding: .utf8)!)")
}
}.resume()
}
解决方案
我需要使用从 .p12 文件转换的 PEM 密钥切换到 RSA 和 RS256。使用 JWT.io 库有所帮助。这是我的工作代码:
import JWTKit // from jwt.io
func generateJWT() -> String? {
struct Header: JWTPayload {
enum CodingKeys: String, CodingKey {
case alg = "alg"
case type = "typ"
}
var alg = "RS256"
var type: String = "JWT"
func verify(using signer: JWTSigner) throws {
// print(self.expireTime > Date().timeIntervalSince1970)
fatalError()
}
}
struct Payload: JWTPayload {
enum CodingKeys: String, CodingKey {
case email = "iss"
case scope = "scope"
case aud = "aud"
case createdAt = "iat"
case expireTime = "exp"
}
var email: String = "content-uploader@*************.iam.gserviceaccount.com"
var scope: String = "https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/cloud-platform"
var aud: String = "https://oauth2.googleapis.com/token"
var createdAt: Double = Date().timeIntervalSince1970
var expireTime: Double = Date().advanced(by: 1000).timeIntervalSince1970
func verify(using signer: JWTSigner) throws {
print(self.expireTime > Date().timeIntervalSince1970)
fatalError()
}
}
do {
if let certificatePath = Bundle.main.path(forResource: "pem-file-name", ofType: "pem") {
let certificateUrl = URL(fileURLWithPath: certificatePath)
let certififcateData = try Data(contentsOf: certificateUrl)
let signers = JWTSigners()
let key = try RSAKey.private(pem: certififcateData)
signers.use(.rs256(key: key))
// MARK: HEADER NOT USED
let header = Header()
let payload = Payload()
let jwt = try signers.sign(payload)
// let jwt = try signers.sign(payload)
print("JWT: \(jwt)")
return jwt
} else {
return nil
}
} catch {
print(error)
return nil
}
}
推荐阅读
- php - symfony 断言使用 if/else 语句验证原始数据
- sql - 我怎样才能每天只获得一个 SQL 查询?
- azure-ad-b2c - 无法让 Azure AD B2C 发出令牌并将其重定向到 https://jwt.ms
- javascript - Firebase 列出所有用户
- vue.js - 在本地机器上加载 Vue 应用程序时出现问题,没有得到所需的结果
- python - 如何在文本文件中附加打印的脚本?
- php - htaccess 问题。一条规则有效,另一条无效
- html - Jquery 'click' 功能仅适用于“if”语句,不适用于“else if”
- linux - Linux NetEm-测量的数据包损坏方式太低
- javascript - Highcharts 树状图