Swift Vapor unsupported_grant_type неверная подпись / токен доступа OAuth

Я использую Xcode 8.1 с Vapor и SWIFT 3.

Я отправляю запрос на сервер Google для получения токена аутентификации, поэтому я могу вызвать FireBaseDB API, но получаю сообщение об ошибке: unsupported_grant_type / Invalid grant_type.
На developers.google.com говорится, что мне нужно закодировать в URL-адресе следующее: https://www.googleapis.com/oauth2/v4/token + grant_type + assertion и передать закодированный URL-адрес в теле запроса POST. . Я передаю это как строку.

Я заметил, что закрытый ключ из файла 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":"[email protected]",
     "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"
}

Обновлять

Согласно предложению Кароля Гасеницы, я передаю параметры grant_type и assertion в качестве параметров запроса 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: [:])

person bibscy    schedule 19.11.2016    source источник
comment
Я не знаком с быстрым, но я думаю, что ваш параметр alg может быть неправильным. Вы указываете (RSASSA-PKCS1-v1_5 с хешем SHA-256 (он же RS256), но вы, кажется, кодируете jwt с помощью HMAC SHA-256 (HS256). Я предлагаю вам заменить "alg":"RS256" на "alg":"HS256", поскольку HS256 является только обязательным alg [0 ] tools.ietf.org/html/rfc7519#section-8   -  person themihai    schedule 20.11.2016


Ответы (2)


При создании учетных данных учетной записи службы необходимо иметь в виду следующие , взятые из https://cloud.google.com/storage/docs/authentication: Вы можете создать закрытый ключ в консоли Cloud Platform, создав идентификатор клиента OAuth для учетной записи службы. Вы можете получить свой закрытый ключ в формате JSON и PKCS12:

Ключи JSON необходимы, если вы используете учетные данные приложения по умолчанию в производственной среде за пределами Google Cloud Platform. Ключи JSON нельзя преобразовать в другие форматы. PKCS12 (.p12) поддерживается многими различными языками программирования и библиотеками. При необходимости вы можете преобразовать ключ в другие форматы с помощью OpenSSL (см. Преобразование закрытого ключа в другие форматы). Однако ключи PKCS12 нельзя преобразовать в формат JSON.

  1. Создайте учетную запись службы, а затем загрузите файл .p12.
  2. Преобразуйте файл p.12 (он же pkcs12) в .pem (он же pkcs1) с помощью OpenSSL

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

  1. Перейдите в github, выполните поиск по VaporJWT и импортируйте его в Xcode. Это поможет вам создать подписанный веб-токен JSON.

  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. Затем вы можете выполнять вызовы Google API с подписанным JWT.

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":"[email protected]",
     "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"
}
person bibscy    schedule 22.11.2016

Похоже, вы неправильно установили grant_type в своем коде:

urlString.append("?grant_type=")

В вашем случае это может быть grant_type=authorization_code или grant_type=jwt-bearer

Похоже, вы установили grant_type не в том месте.

Обновить

Также я думаю, что параметры grant_type и assertion передаются не как заголовки запроса, а как параметры почтового запроса.

Обновить

Я не совсем уверен, что вы используете правильный способ установки параметров POST (body). В этой документации приведен пример создания запроса с колясками для почты, и это выглядит следующим образом :

try drop.client.request(.other(method: "POST"), "http://some-domain", headers: ["My": "Header"], query: ["key": "value"], body: [])
person Karol Gasienica    schedule 19.11.2016
comment
Является ли свойство 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.Можете ли вы пошагово объяснить мне в коде, что мне делать? Спасибо - person bibscy; 20.11.2016
comment
Любая идея, почему сервер OAuth не видит, что я делаю запрос POST https? См. Мой обновленный вопрос. - person bibscy; 20.11.2016
comment
Проверьте сейчас, я вижу, вы помещаете все свои данные в запрос (URL), и он должен быть в POST - person Karol Gasienica; 20.11.2016
comment
Я все еще получаю сообщение об ошибке неподдерживаемый grant_type. Я думаю, что var urlString не отформатирован с использованием правильных компонентов. Я пытаюсь достичь предоставления учетных данных клиента. В файле JSON у меня нет client_secret ключа, но есть private_key_id. Я помещаю код в краткое содержание здесь. Думаю, мне больше не нужно указывать googleapis.com/oauth2/v4/token в тело запроса POST, поскольку оно уже указано в uri запроса POST. - person bibscy; 20.11.2016
comment
createUrlWithString разве эта функция не создает просто строку? В документации указано, что сообщение может быть отправлено с помощью параметра body: [], как указано в примере: try drop.client.request(.other(method: "POST"), "http://some-domain", headers: ["My": "Header"], query: ["key": "value"], body: [ here_post_data]), но вы просто помещаете URL-адрес String(describing: bodyURL) без каких-либо данных сообщения в body. URL не может содержать данные POST. - person Karol Gasienica; 20.11.2016
comment
createUrlWithString создает URL-адрес типа: NSURL. Я мог бы просто -> String в функции выше, а затем передать строку в теле запроса POST. Я не могу передать URL-адрес в теле запроса POST, поэтому преобразовал его в строку. Итак, я передаю в теле запроса POST экземпляр String, который принимает в качестве аргумента var bodyURL , тем самым создавая объект типа String с содержимым bodyURL. - person bibscy; 20.11.2016
comment
Строка, которую я передаю в теле запроса POST, выглядит так: https://www.googleapis.com/oauth2/v4/token?grant_type=client_credentials&client_id=1017561806708243261&private_key_id=8b157a8d8b6b4fde8aaa37bff94bc6cbdb73f3&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwczpcL1wvd3d3Lmdvb2dsZWFwaXMdsZXC9maXJlYmFzZS5kYXRhYmFzZSJ9.Td2v09dvLFgsaEVBRQh8HCRF+GdBNOOqisdj2XY9z4g= - person bibscy; 20.11.2016
comment
grant_type=client‌​_credentials по-прежнему выглядит так, как будто он находится в запросе, а не в теле. Так что это $_GET не $_POST, даже если вы настроили отправку данных как POST. В POST вы можете иметь данные GET и POST - person Karol Gasienica; 20.11.2016
comment
Я использовал print (bodyURL), чтобы показать вам, как выглядит последняя строка. Строка, которую вы видите выше, является значением bodyURL после объединения всех компонентов и передачи bodyURL в качестве аргумента параметру body функции .request (). - person bibscy; 20.11.2016
comment
Вы знаете, как я могу извлечь буквальное значение из private_key, который я загрузил из своей учетной записи службы Google? Оказывается, он закодирован в формате pem из того, что я читал. myfile - person bibscy; 22.11.2016
comment
Не очень жаль :) - person Karol Gasienica; 22.11.2016
comment
Я нашел решение. Смотрите мой ответ :) - person bibscy; 23.11.2016
comment
Любая идея, почему я получаю 403 Permission Denied, когда я пытаюсь получить доступ к базе данных firebase? Вот мой вопрос. stackoverflow.com/questions/40810233 - person bibscy; 26.11.2016