Xero-Api and Vapor 3 unable to connect to recive token

Viewed 142

I have a vapor3 app within which I am trying to connect to the Xero Api, the process is fairly simple. We send the user to a xero web page where they login with the details and authorise our connection, it then redirects back to my vapor 3 app. In the app we then connect to the xero server with the code provided in the redirect, and then xero should be issue an access token to be used going forward. The process is described here

The issue is I can't connect to xero to get the access token. I have tried 2 approaches, the first is to use HTTPClient, with this code:

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

With this I get the following response:

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

Which suggests the end point has moved but the location suggested is the same one that I am trying to connect to. I can't find anywhere in the docs which suggested the end point has changed. It also seems HTTPClient wont follow redirects.

So I tried to connect using URLSession instead using this code:

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()

Where i get the following error message.

{"error":"invalid_request"}

Any ideas what is going on here or how I can get this connection up and running are greatly apprecitated.

1 Answers

Ok so the answer in the end was to use the vapor client service found at req.client()

I used the following code. I also made a rookie error of encodeing the client id and client secret and then combing them, rather then cobining them first then encoding them. The docs do show this but I guess not clear enough. Anyway here is the code that worked to connect to the Xero Api with 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 

}

Hopefully this is helpful to anyone trying to add xero with Vapor.

Related