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

时间:2016-11-19 17:46:15

标签: swift oauth-2.0 vapor

我正在使用Vapor和SWIFT 3运行Xcode 8.1。

我正在向google服务器发送请求以获取身份验证令牌,因此我可以调用FireBaseDB API,但是我收到错误:unsupported_grant_type / invalid grant_type。
developers.google.com上,它表示我必须在URL中编码以下内容:https://www.googleapis.com/oauth2/v4/token + grant_type + assertion,并在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: [:])

2 个答案:

答案 0 :(得分:1)

生成服务帐户凭据时,您需要牢记以下,取自https://cloud.google.com/storage/docs/authentication: 您可以通过为服务帐户创建OAuth客户端ID在云平台控制台中创建私钥。您可以使用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)

  3. 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。

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

答案 1 :(得分:0)

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

urlString.append("?grant_type=")

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

好像你在错误的地方设置了grant_type

<强>更新

此外,我认为grant_type和断言参数不作为请求标头传递,而是作为发布请求参数

传递

<强>更新

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

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