1

I am running Xcode 8.1 with Vapor and SWIFT 3.

I am posting a request to to google server to get an auth token, so I can call FireBaseDB API, but I get error: unsupported_grant_type/Invalid grant_type.
On developers.google.com it says that I have to encode in a URL the following: https://www.googleapis.com/oauth2/v4/token + grant_type + assertion, and pass the encoded URL in the body of the POST request. I pass it as a string.

I have noticed that the private key from the JSON file downloaded from my service account contains characters such as /n , ----,==, should I delete them before posting the key?

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

Update

As per Karol Gasienica suggestion, I am passing grant_type and assertion parameters as POST request parameters. Now I get "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: [:])
4

2 回答 2

1

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

如果您在 Google Cloud Platform 之外的生产环境中使用应用程序默认凭据,则需要 JSON 密钥。JSON 密钥不能转换为其他格式。 许多不同的编程语言和库都支持 PKCS12 (.p12)。如果需要,您可以使用 OpenSSL 将密钥转换为其他格式(请参阅将私钥转换为其他格式)。但是,PKCS12 密钥无法转换为 JSON 格式。

  1. 创建您的服务帐户,然后下载您的 .p12 文件。
  2. 使用 OpenSSL 将 p.12(又名 pkcs12)文件转换为 .pem(又名 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"
}
于 2016-11-22T21:35:14.080 回答
0

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

urlString.append("?grant_type=")

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

好像你放grant_type错地方了。

更新

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

更新

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

try drop.client.request(.other(method: "POST"), "http://some-domain", headers: ["My": "Header"], query: ["key": "value"], body: [])
于 2016-11-19T20:04:19.370 回答