【问题标题】:Swift Vapor unsupported_grant_type invalid signature / OAuth access tokenSwift Vapor unsupported_grant_type 无效签名/OAuth 访问令牌
【发布时间】:2016-11-19 17:46:14
【问题描述】:

我正在运行带有 Vapor 和 SWIFT 3 的 Xcode 8.1。

我正在向谷歌服务器发布请求以获取身份验证令牌,因此我可以调用 FireBaseDB API,但我收到错误:unsupported_grant_type/Invalid grant_type。
developers.google.com 上,它说我必须在 URL 中编码以下内容: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: [:])

【问题讨论】:

  • 我对 swift 不熟悉,但我认为您的 alg 参数可能是错误的。您使用 SHA-256 散列(又名“RS256”)指定(RSASSA-PKCS1-v1_5),但您似乎使用 HMAC SHA-256(“HS256”)对 jwt 进行编码。我建议您将 "alg":"RS256" 替换为 "alg":"HS256"因为 HS256 只是强制 alg [0] tools.ietf.org/html/rfc7519#section-8

标签: swift oauth-2.0 vapor


【解决方案1】:

当您生成服务帐户凭据时,您需要牢记以下,取自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 将 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。

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"
}

【讨论】:

    【解决方案2】:

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

    urlString.append("?grant_type=")
    

    在您的情况下,它可能是grant_type=authorization_code 要么 grant_type=jwt-bearer

    您似乎将grant_type 设置在错误的位置。

    更新

    另外我认为grant_type和assertion参数不是作为请求头而是作为post请求参数传递的

    更新

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

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

    【讨论】:

    • grant_type 属性是字典数组吗?例如 var grant_type = ["urn":"value1","grant-type":"val2", "jwt-bearer":"val3"] 。 dev.google 上不是很清楚。来自ietf stadanrd“grant_type”的值是“urn:ietf:params:oauth:grant-type:jwt-bearer”。你能用代码一步一步解释我该怎么做吗?谢谢
    • 知道为什么 OAuth 服务器看不到我发出 https POST 请求吗?请查看我更新的问题。
    • 现在检查,我看到您将所有数据放入查询(URL)中,它应该在 POST 中
    • 我仍然收到错误 unsupported grant_type。我认为var urlString 没有使用正确的组件进行格式化。我想要实现的是Client credentials grant。在 JSON 文件中,我没有 client_secret 键,但是 private_key_id 。我把代码放在一个要点 here 中。我想我不再需要在 POST 请求的正文中指定 googleapis.com/oauth2/v4/token,因为它已经在 POST 的 uri 中指定了。
    • createUrlWithString 这个函数不只是创建一个字符串吗?文档说可以在body: [] 选项中发送帖子,如示例:try drop.client.request(.other(method: "POST"), "http://some-domain", headers: ["My": "Header"], query: ["key": "value"], body: [ here_post_data]) 但您只是在body 中放置一个没有任何帖子数据的网址String(describing: bodyURL)。 URL 不能包含 POST 数据。
    猜你喜欢
    • 2018-09-24
    • 1970-01-01
    • 2019-09-14
    • 2011-10-25
    • 2018-01-12
    • 2011-09-28
    • 1970-01-01
    • 2011-04-25
    • 2022-09-27
    相关资源
    最近更新 更多