Xero-Api и Vapor 3 не могут подключиться к полученному токену

У меня есть приложение steam3, в котором я пытаюсь подключиться к Xero Api, процесс довольно прост. Мы отправляем пользователя на веб-страницу xero, где он входит в систему с данными и авторизует наше соединение, а затем перенаправляется обратно в мое приложение Steam 3. Затем в приложении мы подключаемся к серверу xero с кодом, указанным в перенаправлении, а затем xero должен выдать токен доступа, который будет использоваться в будущем. Процесс описан здесь

Проблема в том, что я не могу подключиться к xero, чтобы получить токен доступа. Я пробовал 2 подхода, первый - использовать HTTPClient с этим кодом:

 let m = try decoder.decode(Master.self, from: masterDoc!)
 let ci = m.xeroAppKey.data(using: .utf8)?.base64EncodedString() //convert from string to base encoded
 let xs = m.xeroAppSec.data(using: .utf8)?.base64EncodedString() //convert from string to base encoded
 let authorization = "Basic " + ci! + ":" + xs!
 print("authorisation is \(authorization)")
 return HTTPClient.connect(hostname: "identity.xero.com", on: req).flatMap{client in
       var httpReq = HTTPRequest(method: .POST, url: "https://identity.xero.com/connect/token")
       httpReq.headers.add(name: "authorization", value: authorization)
       httpReq.headers.add(name: "Content-Type", value: "x-www-form-urlencoded")
       httpReq.body = HTTPBody(string: "grant_type=authorization_code&code=\(code)&redirect_uri=http://localhost:8080/XeroAuthRedirect")

       return client.send(httpReq).flatMap{resp in
         print("response is \(resp) with status \(resp.status)")
         return req.future().map{
           return .ok
         }  
      }
    } 

На это я получаю такой ответ:

HTTP/1.1 301 Moved Permanently
Server: AkamaiGHost
Content-Length: 0
Location: https://identity.xero.com/connect/token
Expires: Tue, 02 Jun 2020 07:59:59 GMT
Cache-Control: max-age=0, no-cache, no-store
Pragma: no-cache
Date: Tue, 02 Jun 2020 07:59:59 GMT
Connection: keep-alive

Это говорит о том, что конечная точка переместилась, но предложенное место совпадает с тем, к которому я пытаюсь подключиться. Я не могу найти нигде в документах, которые предполагали, что конечная точка изменилась. Также кажется, что HTTPClient не будет следовать перенаправлениям.

Поэтому я попытался подключиться с помощью URLSession вместо этого кода:

let url = URL(string:"https://identity.xero.com/connect/token")!
                let payload = "grant_type=authorization_code&code=\(code)&redirect_uri=http://localhost:8080/XeroAuthRedirect".data(using: .utf8)

                let promise = req.eventLoop.newPromise(HTTPStatus.self)

                var request = URLRequest(url: url)
                request.httpMethod = "POST"
                request.addValue(authorization, forHTTPHeaderField: "authorization")
                request.addValue("x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
                request.httpBody = payload

                URLSession.shared.dataTask(with: request) {(data, response, error) in

                    if let error = error {
                        print(error.localizedDescription)
                        promise.fail(error: error) // (!)
                        return
                    }

                    guard let data = data else {
                        print("empty data")
                        promise.fail(error: SumUpError.invalidResponse) // (!)
                        return
                    }

                    guard let str = String(data: data, encoding: .utf8) else {
                        print("invalid data")
                        promise.fail(error: SumUpError.invalidResponse) // (!)
                        return
                    }

                    print(str)


                    print("response is \(String(describing: response))")


                }.resume()

Где я получаю следующее сообщение об ошибке.

{"error":"invalid_request"}

Мы очень ценим любые идеи, что здесь происходит или как я могу установить и запустить это соединение.


person Joby Ingram-Dodd    schedule 02.06.2020    source источник
comment
Я сомневаюсь, что это что-то актуальное, но в моем коде обновления токена мой заголовок типа содержимого установлен на application/x-www-form-urlencoded, что немного отличается от вашего.   -  person droopsnoot    schedule 02.06.2020
comment
@droopsnoot это не радует. Я предполагаю, что в вашем приложении вы собираетесь на конечную точку identity.xero.com?   -  person Joby Ingram-Dodd    schedule 02.06.2020
comment
Да, но у меня соединение m2m службы Windows, поэтому я не выполняю первоначальный вызов страницы входа в Xero, а вместо этого использую их инструмент XOAuth для получения токенов. Итак, я обновляю только токен, который у меня уже есть. Моя конечная точка такая же, а содержимое тела сообщения отличается.   -  person droopsnoot    schedule 02.06.2020
comment
Еще у меня есть авторизация с большой буквы, но она также содержит слово Bearer перед токеном, так что это Authorization: Bearer & access-token. Опять же, не уверен, чувствителен ли он к регистру или дополнительное слово требуется только в моем сценарии. (О, теперь я вижу, что у вас есть Basic, возможно, это подходит для вашего использования).   -  person droopsnoot    schedule 02.06.2020
comment
@droopsnoot в документации, он показывает, что авторизация должна быть в этом формате authorization: "Basic " + base64encode(client_id + ":" + client_secret), который, как я понял, означает, что я должен закодировать идентификатор и секрет клиента и добавить их в строку с Basic в начале. Вы видите это иначе?   -  person Joby Ingram-Dodd    schedule 02.06.2020
comment
Звучит примерно правильно, но я делаю все немного по-другому. Во-первых, я использую PKCE, поэтому у меня нет секрета клиента. Во-вторых, поскольку я использую XOAuth, я просто сохраняю токен доступа и обновляю токен, а затем продолжаю его обновлять. Однако я не кодирую его в base64, потому что он возвращается из XOAuth и из уже закодированного вызова конечной точки идентификации (для обновления). Это может быть потому, что он возвращается как объект JSON, и, возможно, синтаксический анализ делает это. (Извините, я не знаю точных подробностей, я перестал возиться, когда он начал работать).   -  person droopsnoot    schedule 02.06.2020


Ответы (1)


Итак, в конце концов, ответ заключался в том, чтобы использовать службу клиента Steam, найденную в req.client ()

Я использовал следующий код. Я также сделал ошибку новичка, закодировав идентификатор клиента и секрет клиента, а затем скомбинировав их, а не сначала объединяя их, а затем кодируя. Документы действительно показывают это, но я думаю, недостаточно ясно. В любом случае, вот код, который работал для подключения к Xero Api с помощью Vapor 3.


    let toBase = "\(clientId):\(clientSecret)".data(using: .utf8)?.base64EncodedString()
    let authorization = "Basic " + toBase!           
    let payload = "grant_type=authorization_code&code=\(code)&redirect_uri=http://localhost:8080/XeroAuthRedirect".data(using: .utf8)!
    let request = Request( using: req)
    request.http.url = URL(string:"https://identity.xero.com/connect/token")!
    request.http.body = payload.convertToHTTPBody()
    request.http.method = HTTPMethod.POST
    request.http.headers.add(name: "authorization", value: authorization)
    request.http.headers.add(name: "Content-Type", value: "application/x-www-form-urlencoded")
 return try req.client().send(request).flatMap{resp in 

}

Надеюсь, это будет полезно всем, кто пытается добавить xero с помощью Vapor.

person Joby Ingram-Dodd    schedule 02.06.2020