Swift Vapor unsupported_grant_type 无效的签名/OAuth 访问令牌

Swift Vapor unsupported_grant_type invalid signature / OAuth access token

我是 运行 Xcode 8.1 和 SWIFT 3.

我正在向 google 服务器发送请求以获取身份验证令牌,因此我可以调用 FireBaseDB API,但出现错误:unsupported_grant_type/Invalid grant_type .
developers.google.com it says that I have to encode in a URL the following: https://www.googleapis.com/oauth2/v4/token + grant_type + 断言上,并在 POST 请求的正文中传递编码的 URL 。我将它作为字符串传递。

我注意到从我的服务帐户下载的 JSON 文件中的私钥包含 /n , ----,== 等字符,我应该在发布密钥之前删除它们吗?

    let dateNow = Date()
      var expDate = String(Int(dateNow.timeIntervalSince1970 + (60 * 60)))
        var iatDate = String(Int(dateNow.timeIntervalSince1970))


 let headerJWT = ["alg":"HS256","typ":"JWT"]
    let jwtClaimSet =
    ["iss":"firebase-adminsdk-c7i48@fir-10c2e.iam.gserviceaccount.com",
     "scope":"https://www.googleapis.com/auth/firebase.database",
      "aud":"https://www.googleapis.com/oauth2/v4/token",
       "exp": expDate,
         "iat": iatDate]

      //create and sign JSON Web Token
   let jwt = try JWT(headers: Node(node: headerJWT),
              payload: Node(node: jwtClaimSet),
               signer: HS256(key:"-----BEGIN PRIVATE KEY-----\nMIIEvAWvQ== \n-----END PRIVATE KEY-----\n"))

    // Store JSON Web Token
      let JWTtoken = try jwt.createToken()

func createUrlWithString() -> NSURL {
    var urlString = "https://www.googleapis.com/oauth2/v4/token"
     urlString.append("?grant_type=")
      urlString.append("urn:ietf:params:oauth:grant-type:jwt-bearer")
       urlString.append("&assertion=")
        urlString.append(JWTtoken)
  return NSURL(string: urlString)!
 }

        // make the body input for our POST
      let bodyURL =  createUrlWithString().absoluteURL

     drop.get("any") { request in
        let response =  
 try drop.client.request(.other(method:"Post"),
           "https://www.googleapis.com/oauth2/v4/token", 
             headers: ["Content-Type": "application/x-www-form-urlencoded"], 
                 query: [:], 
                  body: String(describing: bodyURL) )


     let serverResp = response.headers
        let serverBody = response.body.bytes
          let serverJson = try JSON(bytes: serverBody!)
             print(serverJson)
    return "POST Request went through"
}

更新

根据 Karol Gasienica 的建议,我将 grant_typeassertion 参数作为 POST 请求参数传递。现在我得到 "error_description": Node.Node.string("SSL is required to perform this operation.")]))

func createUrlWithString() -> NSURL {
 var urlString = "https://www.googleapis.com/oauth2/v4/token"
  urlString.append("?grant_type=")
    urlString.append("urn:ietf:params:oauth:grant-type:jwt-bearer")
     urlString.append("&assertion=")
      urlString.append(JWTtoken)
  return NSURL(string: urlString)!
}

  let response =  try drop.client.request(.other(method:"Post"), 
   String(describing: bodyURL),
      headers: ["Content-Type": "application/x-www-form-urlencoded"], 
         query: [:])

您的代码似乎没有正确设置grant_type

urlString.append("?grant_type=")

你的情况可能是 grant_type=authorization_code 或者 grant_type=jwt-bearer

你好像把 grant_type 设置错了地方。

更新

另外我认为 grant_type 和断言参数不是作为请求 headers 传递的,而是作为 post 请求参数

更新

我不太确定您是否使用正确的方式放置 POST (body) 参数。在 this 文档中是如何使用 post 婴儿车创建请求的示例,如下所示:

try drop.client.request(.other(method: "POST"), "http://some-domain", headers: ["My": "Header"], query: ["key": "value"], body: [])

当您生成服务帐户凭据时,您需要牢记以下内容,摘自https://cloud.google.com/storage/docs/authentication: 您可以通过为服务帐号创建 OAuth 客户端 ID,在 Cloud Platform Console 中创建私钥。您可以获得 JSON 和 PKCS12 格式的私钥:

如果您在 Google Cloud Platform 之外的生产环境中使用 Application Default Credentials,则需要

JSON 密钥。 JSON 密钥无法转换为其他格式。 许多不同的编程语言和库都支持 PKCS12 (.p12)。如果需要,您可以使用 OpenSSL (see Converting the private key to other formats) 将密钥转换为其他格式。但是,PKCS12 密钥无法转换为 JSON 格式。

  1. 创建您的服务帐户,然后下载您的 .p12 文件。
  2. 使用 OpenSSL

  3. 将 p.12 (a.k.a pkcs12) 文件转换为 .pem (a.k.a pkcs1)

cat /path/to/xxxx-privatekey.p12 | openssl pkcs12 -nodes -nocerts -passin pass:notasecret | openssl rsa > /path/to/secret.pem

  1. 转到github并搜索VaporJWT并将其导入Xcode。它将帮助您创建签名的 JSON Web 令牌。

  2. 在此 github 页面上,您将了解如何提取私钥以供 RSA 使用。

  3. // 将 .pem 转换为 der
    openssl rsa -in /path/to/secret.pem -outform der -out /path/to/private.der

  4. //将.der转换为.base64
    openssl base64 -in /path/to/private.der -out /path/to/Desktop/private.txt
    在 private.txt 中,您拥有以 base64 编码的私钥,您最终可以使用它来签署 JWT。然后您可以使用已签名的 JWT 调用 Google API。

</code></p> <pre><code>import Vapor import VaporJWT let drop = Droplet() var tokenID:String! //set current date let dateNow = Date() // assign to expDate the validity period of the token returnd by OAuth server (3600 seconds) var expDate = String(Int(dateNow.timeIntervalSince1970 + (60 * 60))) // assign to iatDate the time when the call was made to request an access token var iatDate = String(Int(dateNow.timeIntervalSince1970)) // the header of the JSON Web Token (first part of the JWT) let headerJWT = ["alg":"RS256","typ":"JWT"] // the claim set of the JSON Web Token let jwtClaimSet = ["iss":"firebase-adminsdk-c7i38@fir-30c9e.iam.gserviceaccount.com", "scope":"https://www.googleapis.com/auth/firebase.database", "aud":"https://www.googleapis.com/oauth2/v4/token", "exp": expDate, "iat": iatDate] //Using VaporJWT construct a JSON Web Token and sign it with RS256 algorithm //The only signing algorithm supported by the Google OAuth 2.0 Authorization Server is RSA using SHA-256 hashing algorithm. let jwt = try JWT(headers: Node(node: headerJWT), payload: Node(node:jwtClaimSet), encoding: Base64URLEncoding(), signer: RS256(encodedKey: "copy paste here what you have in private.txt as explained at point 7 above ")) // create the JSON Web Token let JWTtoken = try jwt.createToken() let grant_type = "urn:ietf:params:oauth:grant-type:jwt-bearer" // this value must not be changed let unreserved = "*-._" let allowed = NSMutableCharacterSet.alphanumeric() allowed.addCharacters(in: unreserved) // percent or URL encode grant_type let grant_URLEncoded = grant_type.addingPercentEncoding(withAllowedCharacters: allowed as CharacterSet) // create a string made of grant_type and assertion. NOTE!!! only grant_type's value is URL encoded. //JSON Web Token value does not need to be URL encoded var fullString = "grant_type=\(grant_URLEncoded!)&assertion=\(JWTtoken)" //pass fullString in the body parameter drop.get("call") { request in let response = try drop.client.post("https://www.googleapis.com/oauth2/v4/token", headers: ["Content-Type": "application/x-www-form-urlencoded"], query: [:],body: fullString) let serverResp = response.headers let serverBody = response.body.bytes let serverJson = try JSON(bytes: serverBody!) print(serverJson) return "Success" }