2017-03 Security Update
As a part of an ongoing effort to make Lightspeed Retail (X-Series) and its API as secure as possible, we have decided to introduce a couple of security-related changes. Both of these changes are introduced to reduce the risk of leaking sensitive information that could lead to compromising the safety of retailer's data.
IMPORTANT: This update will only affect a small number of retailers and integrators. The majority of our integrators are already using the correct methods which are not changing. The methods that are being deprecated are only used by a small fraction of applications.
1. OAuth flow
The second step of the OAuth flow involves exchanging the intermediate access code for a token. It was always recommended that the parameters required for that exchange are delivered as the body of the request. However, it was possible to include them as request parameters in the URL. After the change takes place on the 15th of April 2017, it will no longer be possible to use the URL params, and all the data will have to be delivered inside the body of the request.
2. Authorizing requests
It was always recommended in our documentation that the access token is delivered inside the Authorization header. However, for testing purposes, it was also possible to authorize an API request by submitting the token in the URL as below:
https://<<domain_prefix>>.retail.lightspeed.app/api/products?access_token=5PRfe98oqBZ0McC9jsf8PI:xsQyIL6mmrl0BafXM
This method is being deprecated and after the 15th of April 2017 it will only be possible to submit the token inside the header.
Code samples
We've prepared some code samples to illustrate the old, deprecated method and what the new code may look like.
OAuth flow - exchanging the code
for the token
code
for the tokenDeprecated:
curl -X GET "https://<<domain_prefix>>.retail.lightspeed.app/api/1.0/token?code={code}&client_id={client_id}&client_secret={client_secret}&grant_type=authorization_code&redirect_uri={redirect_uri}"
Correct:
- cURL:
curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'code={code}&client_id={client_id}&client_secret={client_secret}&grant_type=authorization_code&redirect_uri={redirect_uri}' "https://<<domain_prefix>>.retail.lightspeed.app/api/1.0/token"
- PHP:
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://<<domain_prefix>>.retail.lightspeed.app/api/1.0/token",
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "code={code}&client_id={client_id}&client_secret={client_secret}&grant_type=authorization_code&redirect_uri={redirect_uri}",
CURLOPT_HTTPHEADER => array(
"content-type: application/x-www-form-urlencoded",
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
- C# (using RestSharp):
var client = new RestClient("https://<<domain_prefix>>.retail.lightspeed.app/api/1.0/token");
var request = new RestRequest(Method.POST);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddParameter("application/x-www-form-urlencoded", "code={code}&client_id={client_id}&client_secret={client_secret}&grant_type=authorization_code&redirect_uri={redirect_uri}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
- Ruby:
require 'uri'
require 'net/http'
url = URI("https://<<domain_prefix>>.retail.lightspeed.app/api/1.0/token")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["content-type"] = 'application/x-www-form-urlencoded'
request.body = "code={code}&client_id{client_id}&client_secret={client_secret}&grant_type=authorization_code&redirect_uri={redirect_uri}"
response = http.request(request)
puts response.read_body
- Python:
import http.client
conn = http.client.HTTPSConnection("<<domain_prefix>>.retail.lightspeed.app")
payload = "code={code}&client_id={client_id}&client_secret={client_secret}&grant_type=authorization_code&redirect_uri={redirect_uri}"
headers = {
'content-type': "application/x-www-form-urlencoded"
}
conn.request("POST", "/api/1.0/token", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
- Go:
package main
import (
"fmt"
"strings"
"net/http"
"io/ioutil"
)
func main() {
url := "https://<<domain_prefix>>.retail.lightspeed.app/api/1.0/token"
payload := strings.NewReader("code={code}&client_id={client_id}&client_secret={client_secret}&grant_type=authorization_code&redirect_uri={redirect_uri}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("content-type", "application/x-www-form-urlencoded")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
Using the token to authorise the request
Deprecated:
curl -X GET -H "Content-Type: application/json" "https://<<domain_prefix>>.retail.lightspeed.app/api/products?access_token=5PRfe98oqBZ0McC9jsf8PI:xsQyIL6mmrl0BafXM"
Correct:
- cURL:
curl -X GET -H "Content-Type: application/json" -H "Authorization: Bearer 5PRfe98oqBZ0McC9jsf8PI:xsQyIL6mmrl0BafXM" "https://<<domain_prefix>>.retail.lightspeed.app/api/products"
- PHP:
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://<<domain_prefix>>.retail.lightspeed.app/api/products",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => array(
"authorization: Bearer 5PRfe98oqBZ0McC9jsf8PI:xsQyIL6mmrl0BafXM",
"content-type: application/json",
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
- C# (using RestSharp):
var client = new RestClient("https://<<domain_prefix>>.retail.lightspeed.app/api/products");
var request = new RestRequest(Method.GET);
request.AddHeader("authorization", "Bearer 5PRfe98oqBZ0McC9jsf8PI:xsQyIL6mmrl0BafXM");
request.AddHeader("content-type", "application/json");
IRestResponse response = client.Execute(request);
- Ruby:
require 'uri'
require 'net/http'
url = URI("https://<<domain_prefix>>.retail.lightspeed.app/api/products")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["content-type"] = 'application/json'
request["authorization"] = 'Bearer 5PRfe98oqBZ0McC9jsf8PI:xsQyIL6mmrl0BafXM'
response = http.request(request)
puts response.read_body
- Python:
import http.client
conn = http.client.HTTPSConnection("<<domain_prefix>>.retail.lightspeed.app")
headers =
{
'content-type': "application/json",
'authorization': "Bearer 5PRfe98oqBZ0McC9jsf8PI:xsQyIL6mmrl0BafXM",
}
conn.request("GET", "/api/products", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
- Go:
package main
import (
"fmt"
"strings"
"net/http"
"io/ioutil"
)
func main() {
url := "https://<<domain_prefix>>.retail.lightspeed.app/api/products"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("content-type", "application/json")
req.Header.Add("authorization", "Bearer 5PRfe98oqBZ0McC9jsf8PI:xsQyIL6mmrl0BafXM")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}