Skip to content

OAuth2 Customer Authentication

This guide explains how to authenticate users using OAuth2 in our system. It covers the authorization flow, the use of PKCE (code_challenge), and how to obtain your client credentials.


To use OAuth2, you need a Client ID. You can find or create your API client credentials in the admin panel:

  1. Go to Administration in the backend.
  2. Navigate to Users & Roles.
  3. Click on the Manage dropdown.
  4. Select API Clients.
  5. Here you can view, create, or manage your API clients and obtain the Client ID.

Our API uses the OAuth2 Authorization Code flow with PKCE. This is a secure way to authenticate users, especially for mobile and single-page applications.

This flow means that you will arrive at a WhiteBeard-hosted login page.

Step 1: Generate a Code Verifier and Code Challenge

Section titled “Step 1: Generate a Code Verifier and Code Challenge”

If you are using a public key, the Code Challenge is required. Follow these steps. If you are using a private client, you can skip this step.

  • Code Verifier: A random string (43-128 characters).
  • Code Challenge: A Base64-URL-encoded SHA256 hash of the code verifier.

Example (in PHP):

$codeVerifier = bin2hex(random_bytes(64));
$codeChallenge = rtrim(strtr(base64_encode(hash('sha256', $codeVerifier, true)), '+/', '-_'), '=');

Example (in JavaScript):

async function generateCodeVerifierAndChallenge() {
// Generate a random code verifier (hex string of 64 random bytes → 128 characters)
const randomBytes = new Uint8Array(64);
crypto.getRandomValues(randomBytes);
const codeVerifier = Array.from(randomBytes)
.map(byte => byte.toString(16).padStart(2, '0'))
.join('');
// Hash the code verifier using SHA-256
const encoder = new TextEncoder();
const data = encoder.encode(codeVerifier);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
// Base64-url encode the hash
const codeChallenge = btoa(String.fromCharCode(...new Uint8Array(hashBuffer)))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
return [codeVerifier, codeChallenge];
}

Step 2: Redirect User to the Authorization Endpoint

Section titled “Step 2: Redirect User to the Authorization Endpoint”

Send the user to the /oauth2/authorize endpoint with the following parameters:

  • response_type=code
  • client_id=YOUR_CLIENT_ID
  • redirect_uri=YOUR_CALLBACK_URL
  • code_challenge=CODE_CHALLENGE
  • code_challenge_method=S256
  • scope=... (optional)
  • state=... (recommended for CSRF protection)

Example URL:

https://api.yourdomain.com/oauth2/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_CALLBACK_URL&code_challenge=CODE_CHALLENGE&code_challenge_method=S256&state=xyz

The user logs in and approves access. They are redirected to your redirect_uri with a code parameter.

Make a POST request to /oauth2/access_token with:

  • grant_type=authorization_code
  • client_id=YOUR_CLIENT_ID
  • redirect_uri=YOUR_CALLBACK_URL
  • code=CODE_FROM_STEP_3
  • code_verifier=YOUR_CODE_VERIFIER

Example (cURL):

curl -X POST https://yourdomain.com/oauth2/access_token \
-d 'grant_type=authorization_code' \
-d 'client_id=YOUR_CLIENT_ID' \
-d 'redirect_uri=YOUR_CALLBACK_URL' \
-d 'code=CODE_FROM_STEP_3' \
-d 'code_verifier=YOUR_CODE_VERIFIER'

The authorization server will respond with a JSON object containing the following properties:

  • token_type with the value Bearer
  • expires_in with an integer representing the TTL of the access token
  • access_token a new JWT signed with the authorization server’s private key
  • refresh_token an encrypted payload that can be used to refresh the access token when it expires.

You can use the less-safe method of username/password-based authentication. In this case, you can set the following values on the /oauth2/access_token request:

  • grant_type=password
  • client_id=YOUR_CLIENT_ID
  • client_secret=CLIENT_SECRET
  • username=CUSTOMER_USERNAME
  • password=CUSTOMER_PASSWORD
  • scope=basic

The authorization server will respond with a JSON object containing the following properties:

  • token_type with the value Bearer
  • expires_in with an integer representing the TTL of the access token
  • access_token a new JWT signed with the authorization server’s private key
  • refresh_token an encrypted payload that can be used to refresh the access token when it expires

You can login users without a password, using a one-time pin (OTP). Follow these steps:

  1. Do a POST /customer/login/request. API reference.
    1. If you are using a confidential/private OAuth2.0 client, you must send the client_password field.
    2. If you are using a public client, follow the same Auth Code flow instructions for generating a code_challenge with code_challenge_method=S256.
  2. If your request body is valid, you will always receive HTTP 200 with {"completed": true} regardless of whether the customer account exists or not, and whether you are rate-limited or not. See the notes below about rate-limiting.
  3. If the e-mail exists in the system, automations with the trigger Customer login request are triggered.
    1. Configure an Automation for this trigger.
    2. Set it to send the customer an e-mail.
    3. Use the macro {{code}} in a text layer to show the code.
  4. Once the user types in the code on your frontend, perform POST /oauth2/access_token. API reference.
    1. The same rules apply for client_secret and code_verifier.
    2. grant_type must be set to otp.
    3. username must be set type the email of the user.
    4. You will receive an access_token and refresh_token if successful.
  • Each e-mail address is rate-limited at 1 login request per minute.
  • OTP login codes are valid for 2 minutes. We aim to increase this to 10 minutes based on customer feedback.
  • A maximum of 5 attempts are allowed for a code. After that, it is destroyed.
  • When a failed attempt occurs, the code is set to expire 1 minute from the current time. Consecutive failed attempts will keep moving the expiry forward by 1 minute to a maximum of 5 attempts or a maximum of 10 minutes of continuous attempts.
  • We plan to add IP-based rate-limiting which will require you to forward the Customer’s IP address in the case of a confidential client.

4. External authentication (Facebook Login, Google Login, Apple Login)

Section titled “4. External authentication (Facebook Login, Google Login, Apple Login)”

We also support logging in via third-party authenticators. The currently supported providers are:

  • Facebook Login
  • Google Login
  • Apple Login

Your custom setup can also be supported as an authentication backend. Contact our support for more information.

The authentication flow is as follows:

  1. Implement the third-party’s login flow on your application
  2. Redirect the user to login to that provider
  3. On return, read the access token retrieved (or auth code)
  4. Send a POST to /oauth2/access_token with grant_type=password similar to the password-based authentication. In the username field, you would input the name of the provider. You will need a token or auth code to perform the login. This varies based on the provider. Refer to the examples below.

For Facebook login, use the following values:

  • username=facebook
  • password=JSON_STRING

The JSON string must a JSON-encoded object containing:

  • token: Facebook access token
  • source: The login or user registration source. Used typically when a new registration is triggered.

Example:

{"token":"abcxyz", "source":"website"}

For Apple login, use the following values:

  • username=apple
  • password=JSON_STRING

The JSON string must a JSON-encoded object containing:

  • token: Apple JWT access token
  • name: The full name of the user (read upon initial login or sometimes not provided)
  • source: The login or user registration source. Used typically when a new registration is triggered.

Example:

{"token":"abcxyz", "name": "my full name", "source":"website"}

For Google login, use the following values:

  • username=apple
  • password=JSON_STRING

The JSON string must a JSON-encoded object containing:

  • token: Google access token
  • source: The login or user registration source. Used typically when a new registration is triggered.

Or if you wish for the system to exchange an auth_code on your behalf, you may provide an auth code:

  • auth_code: Auth code from Google OAuth2 process
  • source: The login or user registration source. Used typically when a new registration is triggered.

Example:

{"token":"abcxyz", "name": "my full name", "source":"website"}

Include the access token in the Authorization header for API requests:

Authorization: Bearer ACCESS_TOKEN

To get a new access token from a refresh token, send a POST request with following body parameters to /oauth2/access_token:

  • grant_type with the value refresh_token
  • refresh_token with the refresh token
  • client_id with the the client’s ID
  • client_secret with the client’s secret (API Key)
  • scope with a space-delimited list of requested scope permissions. This is optional; if not sent the original scopes will be used, otherwise you can request a reduced set of scopes.

The authorization server will respond with a JSON object containing the following properties:

  • token_type with the value Bearer
  • expires_in with an integer representing the TTL of the access token
  • access_token a new JWT signed with the authorization server’s private key
  • refresh_token an encrypted payload that can be used to refresh the access token when it expires

Example (cURL):

curl -X POST https://api.yourdomain.com/oauth2/access_token \
-d 'grant_type=refresh_token' \
-d 'client_id=YOUR_CLIENT_ID' \
-d 'client_secret=YOUR_CLIENT_SECRET' \
-d 'scope=basic' \
-d 'refresh_token=YOUR_REFRESH_TOKEN'