Press "Enter" to skip to content

Sign in with Google on BigCommerce Stencil Website

During my internship at Fabspeed, I was tasked to enable customers to sign into a BigCommerce website (made with Stencil, BigCommerce’s theme engine) using their Google account as an alternative to the traditional account creation using an e-mail and password.

How to log into BigCommerce customer accounts

The first thing I had to figure out is: how do you log into customer accounts on BigCommerce? It turns out, you have to create an API account for your BigCommerce store allowing access to customers and customer logins. This is done by going to your BigCommerce store’s dashboard and then going to Advanced Settings > API Accounts > Create API Account > Create V2/V3 API Token, giving your API account a name, for example, Customer Login API then checking the OAuth scopes Customers (modify) and Customers Login (login) and clicking Save. This downloads a text file, e.g., BigCommerceAPI-credentials-storehash-abc123def456-123456789.txt, into your computer’s Downloads folder containing the credentials pertinent to the API account you just created. This file contains the Access Token, Client Name, API Path, Client ID, and Client Secret.

With this information, the next step is to generate JWTs (JSON Web Tokens) which, when encoded, can be used to generate a login URL for the customer’s which will be valid for 30 seconds since the URL JWT was created. The structure of an encoded JWT looks like this: header.payload.signature. To obtain the encoded JWT, we need to write the decoded JWT ourselves. For example, a header would look like

{
  "typ": "JWT",
  "alg": "HS256"
}

and the payload would look like

{
  "iss": "id",
  "iat": 1641415283,
  "jti": "2i8ag3vy18wp26376e64504i40iviqq5",
  "operation": "customer_login",
  "store_hash": "hr7yr173",
  "customer_id": 1,
  "channel_id": 1
}

The signature is generated using the specified hashing algorithm and client secret. The encoded JWT should look something like this: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c.

The entire URL generation can be done via a Node.js script (or other supported language). The instructions for doing so can be found in the BigCommerce Customer Login API Documentation. The script looks like this:

const jwt = require('jsonwebtoken');
const {v4: uuidv4} = require('uuid');
 
function getLoginUrl(customerId, storeHash, storeUrl, clientId, clientSecret) {
   const dateCreated = Math.round((new Date()).getTime() / 1000);
   const payload = {
       "iss": clientId,
       "iat": dateCreated,
       "jti": uuidv4(),
       "operation": "customer_login",
       "store_hash": storeHash,
       "customer_id": customerId,
   }
   let token = jwt.sign(payload, clientSecret, {algorithm:'HS256'});
   return `${storeUrl}/login/token/${token}`;
};
 
const clientId = "Your client id";
const clientSecret = "Your client secret";
const customerId = "Your customer id";
const storeHash = "Your store hash";
const storeUrl = "Your store url";
 
const loginUrl = getLoginUrl(customerId, storeHash, storeUrl, clientId, clientSecret);
console.log(loginUrl);

This script essentially takes the store URL and appends the encoded JWT token to result in something such as https://mystore.com/login/token/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c which can then be used to log into the customer’s account as long as the token hasn’t expired. So the next question is: how do we provide the JWT with the customer ID we want?

Creating a BigCommerce customer

To create a BigCommerce customer, we make a POST request on the https://api.bigcommerce.com/stores/${STORE_HASH}/v3/customers endpoint with the headers

{
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'X-Auth-Token': 'YOUR_AUTH_TOKEN'
}

and body

[
    { 
        'email': 'Your email',
        'first_name': 'Your first name',
        'last_name': 'Your last name'
    }
]

Making a POST request via a client-side XMLHttpRequest will expose the API token, so I had to find a server-side solution for making the POST request. After some research, I found that Netlify provides free serverless functions powered by AWS Lambda which can run Node.js functions without the need for building entire backends from scratch. There are several packages available in npm to make HTTP requests. I used the node-fetch package as suggested in the Netlify’s Up and running with serverless functions tutorial. After following the Netlify tutorial for serverless functions, I created a custom function for customer creation, e.g., netlify/functions/create-customer.js. To create a customer on the BigCommerce store, I use the following code:

import fetch from 'node-fetch';

exports.handler = async function(event, context) {
    const CUSTOMER_API = https://api.bigcommerce.com/stores/${STORE_HASH}/v3/customers;
    const response = await fetch(CUSTOMER_API, {
        method: 'POST',
        body: event.body,
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'X-Auth-Token': `${BIGCOMMERCE_CUSTOMER_API_AUTH_TOKEN}`        }
    });
  
    const data = await response.json();

    return {
        statusCode: 200,
        body: JSON.stringify(data)
    };
}

Calling this function on the client side can be done through the browser’s built-in fetch function:

await fetch('http://localhost:8888/.netlify/functions/create-customer', {
    mode: 'no-cors',
    method: 'POST',
    body: JSON.stringify([{
        'email': customer.email,
        'first_name': customer.given_name,
        'last_name': customer.family_name
    }])
});

When the function is deployed, you can replace http://localhost:8888 with your Netlify app name.

You can also make a serverless function for customer logins as well:

import fetch from 'node-fetch'
import jwt from 'jsonwebtoken';
import { v4 as uuidv4 } from 'uuid';

function getLoginUrl(customerId, storeHash, storeUrl, clientId, clientSecret) {
    const dateCreated = Math.round((new Date()).getTime() / 1000);
    const payload = {
        "iss": clientId,
        "iat": dateCreated,
        "jti": uuidv4(),
        "operation": "customer_login",
        "store_hash": storeHash,
        "customer_id": customerId,
    }
    let token = jwt.sign(payload, clientSecret, { algorithm: 'HS256' });
    return `${storeUrl}/login/token/${token}`;
};

exports.handler = async function (event, context) {
    const email = event.queryStringParameters.email;
    const CUSTOMER_API = `https://api.bigcommerce.com/stores/${STORE_HASH}/v3/customers?email:in=${email}`;
    const response = await fetch(CUSTOMER_API, {
        method: 'GET',
        headers: {
            'Content-Type': 'application/json',
            'X-Auth-Token': `${BIGCOMMERCE_API_AUTH_TOKEN}`
        }
    });
    const data = await response.json();
    
    if (data.data.length < 1) {
        return {
            statusCode: 400,
            body: `Account not found for ${email}`
        }
    }

    const customerId = data.data[0].id;

    const clientId = `${BIGCOMMERCE_API_CLIENT_ID}`;
    const clientSecret = `${BIGCOMMERCE_API_CLIENT_SECRET}`;
    const storeHash = `${STORE_HASH}`;
    const storeUrl = `${STORE_URL}`;
    const loginUrl = getLoginUrl(customerId, storeHash, storeUrl, clientId, clientSecret);

    return {
        statusCode: 200,
        body: loginUrl,
        headers: {
            'Access-Control-Allow-Origin': '*',
            'Access-Control-Allow-Methods': 'GET, PUT, POST, DELETE, OPTIONS',
            'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
        }
    };
}

and the function can be called on the client-side accordingly:

const response = await fetch(
    `https://${NETLIFY_APP_NAME}.netlify.app/.netlify/functions/login-customer?email=${customer.email}`)
    .then(response => response.text());
window.location.href = response;

WARNING: The serverless login function is not secure and your BigCommerce customer accounts will be vulnerable! The user’s ID should be verified before allowing them to login into the provided email.

Helpful guides

Helpful references

Leave a Reply