← All Documentation

Authentication & OAuth

Mercentia uses OAuth 2.0 with PKCE (Proof Key for Code Exchange) for secure app authentication. This ensures merchants can safely grant your app access to their store data.

Overview

Every app interaction with merchant data goes through OAuth. The flow is:

  1. Merchant clicks "Install" on your app listing
  2. Mercentia redirects them to your redirect_uri with an authorization code
  3. Your app exchanges the code for an access token and refresh token
  4. You use the access token in API calls via the Authorization: Bearer header
  5. When the access token expires (24h), refresh it using the refresh token
PKCE Required: All OAuth flows must use PKCE (RFC 7636). This prevents authorization code interception attacks and is required for all app types — server-side, SPA, and mobile.

OAuth 2.0 + PKCE Flow

1. Generate PKCE Challenge

Before redirecting the merchant, generate a code verifier and challenge:

Node.js
import crypto from 'crypto';

// Generate a random code verifier (43-128 chars)
const codeVerifier = crypto.randomBytes(32).toString('base64url');

// Create S256 challenge
const codeChallenge = crypto
  .createHash('sha256')
  .update(codeVerifier)
  .digest('base64url');

// Store codeVerifier in your session — you'll need it in step 3

2. Authorization Request

GET /oauth/authorize
  ?client_id=sf_app_abc123
  &redirect_uri=https://myapp.com/auth/callback
  &scope=read_products,read_orders,read_customers
  &state=random_csrf_token_from_your_session
  &response_type=code
  &code_challenge={codeChallenge}
  &code_challenge_method=S256
ParameterRequiredDescription
client_idYesYour app's client ID from the developer dashboard
redirect_uriYesMust exactly match the URI registered in your app settings
scopeYesComma-separated list of permissions (see Scopes section)
stateYesRandom string for CSRF protection — verify this on callback
response_typeYesMust be code
code_challengeYesS256 hash of your code verifier
code_challenge_methodYesMust be S256

3. Handle Callback

After the merchant approves, Mercentia redirects to your redirect_uri:

GET https://myapp.com/auth/callback
  ?code=sf_auth_xxxxx
  &state=random_csrf_token_from_your_session
  &store_id=uuid_of_merchants_store
Always verify the state parameter matches the one you stored in the session. If it doesn't match, reject the request — it may be a CSRF attack.

4. Token Exchange

POST /oauth/token
Content-Type: application/json

{
  "grant_type": "authorization_code",
  "client_id": "sf_app_abc123",
  "client_secret": "your_client_secret",
  "code": "sf_auth_xxxxx",
  "redirect_uri": "https://myapp.com/auth/callback",
  "code_verifier": "original_code_verifier_from_step_1"
}

5. Token Response

{
  "access_token": "sf_at_xxxxx",
  "refresh_token": "sf_rt_xxxxx",
  "token_type": "Bearer",
  "expires_in": 86400,
  "scope": "read_products,read_orders,read_customers",
  "store_id": "merchant_store_uuid"
}

Scopes

Request only the scopes your app actually needs (least-privilege principle):

ScopeAccessDescription
read_productsReadView products, variants, images, collections
write_productsWriteCreate, update, delete products
read_ordersReadView orders, line items, fulfillments
write_ordersWriteUpdate order status, add notes, create draft orders
read_customersReadView customer profiles, addresses, tags
write_customersWriteCreate, update customer records
read_analyticsReadView store analytics and reports
read_inventoryReadView stock levels and locations
write_inventoryWriteUpdate stock levels
read_shippingReadView shipping zones, rates, carriers
write_shippingWriteCreate labels, update tracking
read_discountsReadView discount codes and promotions
write_discountsWriteCreate, update discount codes
read_contentReadView pages, blog posts, navigation
write_contentWriteCreate, update pages and content
manage_checkoutsWriteModify checkout flow and fields
manage_fulfillmentsWriteCreate and update fulfillments
read_store_settingsReadView store configuration, currencies, languages

Token Management

Access Tokens

  • Expire after 24 hours
  • Use in Authorization: Bearer {token} header
  • Include X-Store-Id: {store_id} header with every request
  • Are scoped — only grant access to the permissions the merchant approved

Refresh Tokens

  • Expire after 90 days
  • Single-use — each refresh gives you a new refresh token
  • If a refresh token is used twice, all tokens for that installation are revoked (replay detection)
  • Store securely — encrypted at rest, never in client-side code

Refreshing Tokens

POST /oauth/token
{
  "grant_type": "refresh_token",
  "client_id": "sf_app_abc123",
  "client_secret": "your_client_secret",
  "refresh_token": "sf_rt_xxxxx"
}

Token Revocation (on Uninstall)

When a merchant uninstalls your app, all tokens are automatically revoked. Your app will receive an app.uninstalled webhook. You must:

  • Delete all stored tokens for this merchant
  • Remove any store data you've cached
  • Stop any background processing for this store

Session API

Verify a token and check current session info:

GET /oauth/session
Authorization: Bearer sf_at_xxxxx

Response:
{
  "store_id": "merchant_store_uuid",
  "app_id": "your_app_uuid",
  "scopes": ["read_products", "read_orders"],
  "expires_at": "2026-04-22T14:30:00Z"
}

Security Best Practices

RequirementDetails
PKCEAlways use S256 code challenge — required for all apps
State parameterGenerate a cryptographically random state and verify on callback
HTTPS onlyRedirect URIs and webhook URLs must use HTTPS
Token storageEncrypt tokens at rest — use AES-256 or your platform's secrets manager
Client secretNever expose in frontend code, logs, or version control
Scope minimisationOnly request permissions your app actually needs
Token refresh Implement automatic refresh before expiry — don't let tokens expire mid-operation
Uninstall cleanupDelete all merchant data and tokens when receiving app.uninstalled

Error Handling

ErrorCodeAction
invalid_client401Check your client_id and client_secret
invalid_grant400Authorization code expired or already used
invalid_scope400Requested scope not registered for your app
access_denied403Merchant denied the permission request
token_expired401Refresh the access token
token_revoked401App was uninstalled — stop using this token
insufficient_scope403Token doesn't have the required permission for this endpoint
rate_limited429Back off and retry after the Retry-After header value