Authentication

Shoplazza API authentication

To make sure all transactions on Shoplazza's platform safe and secure, all apps connected with Shoplazza APIs must authenticated when making API request.

Authentication Types

Different types of Apps use different authentication methods:

  • Public apps use OAuth
  • Private apps use Access-Token

App Definition
Any web application connects with Shoplazza is referred as an App, regardless of how it's exposed to end users.

Please make sure you know the different between the authentication types mentioned above before your kick start your development.

OAuth

Public apps must authenticate using OAuth 2.0 to use Shoplazza's API resources.

Terminology
before we head into the details of OAuth process, please make sure you understand the terminology below:

  • Client: Client refer to Any App who want to access Store Resources.
  • APIShoplazza's RESTful API,Client could use API provided to retrieve and update Store Resources.
  • UserShoplazza Account's owner, also refer as Merchant. User need to authorised Client different permissions to access the Store Resources so that Client could use API to retrieve or update it.
  • App URL: App's main URL that the merchant needs to request when they click to install the public app.
  • Redirect URL: Redirect URL that the merchant is redirected to after completing the installation authorization for the public app. The redirect URL cannot contain the following characters: #.

OAuth Process
Here's a brief process for OAuth 2.0 to authenticate Shoplazza API Request.

  1. The merchant make a request to install the App.
  2. The App redirect to Shoplazza's permission page, which is a prompt page demonstrated the App's name and what scopes it required.
  3. The merchant consents to the scoped permissions and redirect back to the redirect_uri.
  4. The App make a access token request to Shoplazza including code, client_id, and client_secret
  5. Shoplazza return the correct access code
  6. The App use the access_code to make requests to Shoplazza RESTful API
  7. Shoplazza return the requested Data.

Step 1: Obtain the Client ID & Client Secret

To obtain the Client ID & Client Secret, you need to

  1. Register a partner account, follow this guide to register a partner account
  2. Create an app, follow this guide to create an app
  3. After your app is created, you can obtain your Client ID & Client Secret in app settings page

Step 2: Get API Access Token

📘

OAuth SDK Available!

You can use SHOPLAZZA OAuth SDKs to quickly complete the step 2(aka OAuth process), go and checkout our OAuth SDK SHOPLAZZA OAuth SDKs to learn more, also checkout the sub-steps below, SDK tips will be shown in every sub-steps!

1. Ask for permissions

To show the App's prompt page for the merchant to start with, Shoplazza will first call app_uri the app provided in Step 1, and response to redirect the merchant to the following URL with the query parameters defined below:

https://{store_name}.myshoplaza.com/admin/oauth/authorize?client_id={client_id}&scope={scopes}&redirect_uri={redirect_uri}&response_type={response_type}&state={state}
  • store_name: The name of merchant's store.
  • client_id: The Client ID for the app.
  • scope: A space separated list of [scopes](ref:access_sc

opes). For example, to write orders and read customers, use scope=write_order read_customer.

  • redirect_uri: The URL to which a merchant is redirected after authorizing the app.
  • response_type: The response type of OAuth 2.0 process, here we need to fill in code
  • state: a random value, use to prevent CSRF attacks.

📘

OAuth SDK Available!

You can use SHOPLAZZA OAuth SDKs to quickly assemble this URL, for instance: Oauth-SDK-Go, see example below:

// OAuth SDK Example:
import (
  co "github.com/shoplazza-os/oauth-sdk-go"
  "github.com/shoplazza-os/oauth-sdk-go/shoplazza"
)

oauth := &co.Config{
		ClientID:     "s1Ip1WxpoEAHtPPzGiP2rK2Az-P07Nie7V97hRKigl4",
		ClientSecret: "0LFJcNqVb2Z1nVt9xT72vOo0sTWd6j8wVX60Y5xdzZZ",
		Endpoint:     shoplazza.Endpoint,
		RedirectURI:  "https://3830-43-230-206-233.ngrok.io/oauth_sdk/redirect_uri/",
		Scopes:       []string{"read_shop"},
	}
var opts []AuthCodeOption
oauth.AuthCodeURL("xxx.myshoplaza.com", opts...)

2. Confirm Installation

When the merchant clicks the install button in the prompt, they're redirected to your app's server and using redirect_uri provided in Step 2. The authorization_code is passed in the confirmation redirect.

http://example.com/some/redirect_uri?code={authorization_code}&shop={store_name}.myshoplaza.com&hmac={hmac}
Security Checks

Before we continue, make sure your app performs the following security checks. If any of the checks faill, then your app must reject the request with an error, and must not continue.

  • The hmac is valid and signed by Shoplazza
  • The shop parameter is a valid shop hostname, ends with myshoplazza.com

📘

OAuth SDK Available!

For Security Checks, SHOPLAZZA OAuth SDKs also has corresponding methods, you can quickly verify hmac and shop parameter by SHOPLAZZA OAuth SDKs, for instance: Oauth-SDK-Go, see example below:

import (
  co "github.com/shoplazza-os/oauth-sdk-go"
  "github.com/shoplazza-os/oauth-sdk-go/shoplazza"
)

oauth := &co.Config{
		ClientID:     "s1Ip1WxpoEAHtPPzGiP2rK2Az-P07Nie7V97hRKigl4",
		ClientSecret: "0LFJcNqVb2Z1nVt9xT72vOo0sTWd6j8wVX60Y5xdzZZ",
		Endpoint:     shoplazza.Endpoint,
		RedirectURI:  "https://3830-43-230-206-233.ngrok.io/oauth_sdk/redirect_uri/",
		Scopes:       []string{"read_shop"},
	}
oauth.ValidShop("xxx.myshoplaza.com")        // verify shop parameter

var requestUrl = "http://example.com/some/redirect_uri?code={authorization_code}&shop={store_name}.myshoplaza.com&hmac={hmac}"
query := strings.Split(requestUrl, "?")
params, _ := url.ParseQuery(query[1])
oauth.SignatureValid(params)                 // verify hmac
Get a permanent access token

If all security checks pass, then you can exchange the authorization_code for a permanent access token by sending a request to the shop's access_token endpoint:

POST https://{store_name}.myshoplaza.com/admin/oauth/token

In this request, store_name is the name of the merchant's store and alongs with the following parameters:

  • client_id: The Client ID for the App.
  • client_secret: The Client secret key for the app.
  • code: The authorization_code provided in the redirect.
  • grant_type: The grant type of OAuth 2.0 process, please fill in authorization_code here.
  • redirect_uri: The redirect_uri of the app.

The server responds with an access token:

{
  "token_type": "Bearer",
  "expires_at": 1550546245,
  "access_token": "eyJ0eXAiOiJKV1QiLCJh",
  "refresh_token": "def502003d28ba08a964e",
  "store_id": "2",
  "store_name": "xiong1889"
}
  • token_type: It will just return Bearer.
  • expires_at: The access_token expired time, in timestamp.
  • access_token: The correct access_token.
  • refresh_token: The refresh token used to refresh the access_token if needed.
  • store_id: Store's ID in Shoplazza
  • store_name: Store name

📘

OAuth SDK Available!

Similarly, you can quickly get an access token by SHOPLAZZA OAuth SDKs, for instance: Oauth-SDK-Go, see example below:

import (
  co "github.com/shoplazza-os/oauth-sdk-go"
  "github.com/shoplazza-os/oauth-sdk-go/shoplazza"
)

oauth := &co.Config{
		ClientID:     "s1Ip1WxpoEAHtPPzGiP2rK2Az-P07Nie7V97hRKigl4",
		ClientSecret: "0LFJcNqVb2Z1nVt9xT72vOo0sTWd6j8wVX60Y5xdzZZ",
		Endpoint:     shoplazza.Endpoint,
		RedirectURI:  "https://3830-43-230-206-233.ngrok.io/oauth_sdk/redirect_uri/",
		Scopes:       []string{"read_shop"},
	}
token, err := oauth.Exchange(context.Background(),"xxx.myshoplaza.com", "code"))
Refresh the access token

After access_token expired, The app need to call following endpoint to retrieve a new access_token and a new refresh_token ( Please save it into your app and you are gonna need it later)

POST https://{store_name}.myshoplaza.com/admin/oauth/token

In this request, following parameters are needed:

  • client_id: The Client ID that Shoplazza provided.
  • client_secret: The Client secret key that Shoplazza provided.
  • refresh_token: The refresh_token that mentioned above.
  • grant_type: The grant type of OAuth 2.0 process, please fill in refresh_token here.
  • redirect_uri: The redirect_uri of the app.

The server responds with an access token:

{
  "token_type": "Bearer",
  "expires_at": 1550546245,
  "access_token": "eyJ0eXAiOiJKV1QiLCJh",
  "refresh_token": "def502003d28ba08a964e",
  "store_id": "2",
  "store_name": "xiong1889"
}

📘

OAuth SDK Available!

Similarly, you can quickly refresh the access token by SHOPLAZZA OAuth SDKs, for instance: Oauth-SDK-Go, see example below:

import (
  co "github.com/shoplazza-os/oauth-sdk-go"
  "github.com/shoplazza-os/oauth-sdk-go/shoplazza"
)

oauth := &co.Config{
		ClientID:     "s1Ip1WxpoEAHtPPzGiP2rK2Az-P07Nie7V97hRKigl4",
		ClientSecret: "0LFJcNqVb2Z1nVt9xT72vOo0sTWd6j8wVX60Y5xdzZZ",
		Endpoint:     shoplazza.Endpoint,
		RedirectURI:  "https://3830-43-230-206-233.ngrok.io/oauth_sdk/redirect_uri/",
		Scopes:       []string{"read_shop"},
	}
token, err := oauth.RefreshToken(context.Background(), "xxx.myshoplaza.com", "refresh token")

Step 3: Make authenticated requests

After your app has retrieved an API access_token, it can make authenticated request to Admin API.

These request are accompanied with a header Access-Token: {access_token} where {access_token} is replaced with the permanent token.

The following request show how to retrieve a list of products using the Admin API

curl -i -X GET \
     -H "Content-Type:application/json" \
     -H "Access-Token:B_x-_5aVeXNwI-4AB98s5xLIvgv0fNzGf_MuTpqtIBA" \
     'https://store.myshoplaza.com/openapi/2020-01/products'

Private App's Access-Token Authentication

Private App is required to using Access-Token authentication, The Access-Token can be retrieved by creating an Private App record in Store Admin's App Menu. The way to make the authenticated request for an Private App is the same as Public App, but simply just skip the OAuth 2.0 dance process.

Hmac Validation

Every request or redirect from Shoplazza to your app's server includes an hmac parameter that can be used to verify the authenticity of Shoplazza. For each reqeust, you must remove the hmac entry from the query string and process it through an HMAC-SHA256 hash function.

For example, for following request:

http://example.com/some/redirect/uri?code=1vtke5ljOOL2jPds6gM0TNCeYZDitYB&shop=simon.myshoplaza.com&hmac=22bad22eee1f92836f7773e87d973479

To remove the hmac, you can transform the query string to a map, remove the hmac key-value pair, and then lexicographically concatenate your map back to a query string. This leaves the remaining parameters from the example query string:

code=1vtke5ljOOL2jPds6gM0TNCeYZDitYB&shop=simon.myshoplaza.com

Process the hash function
After you remove hmac and reformat the query string, you can process the string through an HMAC-SHA256 hash function using the Client secret Shoplazza provided to your app. The message is authenticated if the generated hexdigest is equal to the value of the hmac parameer.

The following Ruby example show how to process the string through a hash function:

def verified_hmac?(hmac)
  sha256 = OpenSSL::Digest::SHA256.new
  query_string = "code=1vtke5ljOOL2jPds6gM0TNCeYZDitYB&shop=simon.myshoplaza.com"
  calculated_hmac = OpenSSL::HMAC.hexdigest(sha256, CLIENT_SECRET, query_string)
  ActiveSupport::SecurityUtils.secure_compare(calculated_hmac, hmac)
end

Verify a webhook

The HMAC verification process for OAuth is different from the process to verify webhooks.

Webhooks can be verified by calculating a digital signature. Each webhook request include a base64 encoded X-Shoplazza-Hmac-Sha256 header, which is generated using the app's Client Secret along with the data sent in the request.

To verify that the request came from Shoplazza, compute the HMAC digest according to the following algorithm and compare it to the value in the X-Shoplazza-Hmac-Sha256 header. If they match, then you can be sure that the webhook was sent from Shoplazza. As a best practice, the HMAC digest should be verified before the app responds to the webhook.

The following example use Ruby and Sinatra to verify a webhook request:

require 'rubygems'
require 'base64'
require 'openssl'
require 'sinatra'

# Shoplazza's Client Secret
SECRET = 'my_secret'

helpers do
  # Compare the computed HMAC digest based on the shared secret and the request contents to the reported HMAC in the headers
  def verify_webhook(data, hmac_header)
    calculated_hmac = Base64.strict_encode64(OpenSSL::HMAC.digest('sha256', SECRET, data))
    ActiveSupport::SecurityUtils.secure_compare(calculated_hmac, hmac_header)
  end
end

# Responds to the HTTP POST request
post '/' do
  request.body.rewind
  data = request.body.read
  verified = verify_webhook(data, env["X-Shoplazza-Hmac-Sha256"])

  puts "Webhook verified: #{verified}"
end