StoreConnect storefronts use a Liquid-based theme engine, but you can also embed Salesforce Lightning Web Components (LWC) directly into your storefront pages. This lets you bring Salesforce-native UI and logic into your store without rebuilding it in Liquid or JavaScript.

This can be achieved using **Salesforce Lightning Out** combined with a secure, token-based authentication layer. The StoreConnect theme generates an HMAC-signed token server-side, and a wrapper LWC on the Salesforce side verifies the token before dynamically rendering your component with authenticated customer data. Depending on your setup, there are also other ways to approach this.

A ready-to-deploy proof of concept project is available for download:

[Download the StoreConnect Secure LWC project](https://drive.google.com/drive/folders/1ZYi7gNPT13C8F1dBO7-jTMa6Vr2Jr63r?usp=sharing)

## How it works

The embedding process follows a secure handshake between StoreConnect and Salesforce:

1. **Token generation (StoreConnect)** -- When a storefront page loads, the Liquid template generates a JSON payload containing the customer's context (Customer ID, Store ID) and a timestamp. This payload is cryptographically signed using HMAC-SHA256 with a shared secret stored in [Store Variables](store-variables). The resulting token is embedded into the page HTML.

2. **Client-side loading** -- The browser loads the Salesforce Lightning Out library via a singleton loader (preventing duplicate script loads when multiple components are on the same page). A wrapper LWC is initialized and receives the signed token along with the name of the component to render.

3. **Server-side verification (Salesforce)** -- The wrapper LWC passes the token to an Apex controller. Apex retrieves the shared secret for the originating store, recalculates the HMAC signature, and verifies it matches. It also checks that the token has not expired (1-hour window with 30-second clock skew tolerance).

4. **Dynamic rendering** -- Once verified, the wrapper dynamically instantiates the requested business component and passes the authenticated customer data down to it. The component renders inside the storefront page.

```
StoreConnect                                Salesforce
+------------------------------+            +-----------------------------------+
| 1. Page loads                |            |                                   |
| 2. Liquid generates          |            |                                   |
|    HMAC-signed token         |            |                                   |
| 3. Token embedded in HTML    |            |                                   |
|                              |            |                                   |
| 4. Browser loads             |  ------>   | 5. Wrapper LWC receives token     |
|    Lightning Out library     |            |    + component name               |
|                              |            |                                   |
|                              |            | 6. Apex validates:                |
|                              |            |    - Retrieve store secret        |
|                              |            |    - Verify HMAC signature        |
|                              |            |    - Check token expiry           |
|                              |            |                                   |
|                              |  <------   | 7. Render requested component     |
| 8. Resize and error events   |  <------   |    with authenticated data        |
|    via postMessage           |            |                                   |
+------------------------------+            +-----------------------------------+
```

## Security features

- **Tamper-proof** -- Any modification to the token (for example, changing the Customer ID) invalidates the signature and causes immediate rejection.
- **Zero-trust** -- No secrets or API keys are ever exposed to the client browser. The HMAC secret exists only server-side in Store Variables and in the Salesforce `Store_Variable__c` record.
- **Cache compatible** -- The stateless design (timestamp + signature) works with StoreConnect's page caching without triggering false security errors.
- **Component allowlist** -- Only explicitly registered components can be loaded. The wrapper uses a `COMPONENT_MAP` object as a security gate, preventing arbitrary component injection.
- **Constant-time comparison** -- Signature verification uses constant-time string comparison to prevent timing attacks.
- **Token expiry** -- Tokens are valid for 1 hour from generation, with a 30-second clock skew tolerance for server time differences.

## Alternative approaches

Depending on what you are building, you may not need LWC embedding at all. StoreConnect provides several ways to add custom functionality to your storefront:

- **Liquid templates with custom JavaScript** -- Full control over markup and scripting using the theme engine. See [Adding custom JavaScript, CSS and head content](adding-custom-javascript-css-and-head-content).
- **Content blocks with raw HTML/JS** -- Use the "No added styling" content block template to insert custom HTML and JavaScript. See [Content block templates](content-block-templates).
- **Server-side API proxy** -- Use Liquid's `api` tag and Store Variables to call external APIs server-side without exposing credentials. This is useful for fetching and displaying data from third-party services.

LWC embedding is best suited for scenarios where you need to reuse existing Salesforce components, access Salesforce data in real time through Apex, or leverage the Lightning component ecosystem on your storefront.

## Prerequisites

Before you begin, you need:

- A Salesforce org with the StoreConnect managed package installed
- Salesforce CLI (`sf`) installed locally
- An Experience Cloud site configured in your Salesforce org (this provides the Lightning Out endpoint)
- Access to your StoreConnect theme files

## What is in the project

The downloadable project contains everything needed to get LWC embedding working:

| File | Purpose |
|------|---------|
| `templates/blocks/lwc.liquid` | Liquid content block template for your StoreConnect theme. Handles token generation and Lightning Out initialization. |
| `force-app/main/default/classes/StoreConnectAuthHandler.cls` | Apex class that validates HMAC-signed tokens. Retrieves the shared secret from Store Variables, verifies the signature, and checks token expiry. |
| `force-app/main/default/classes/StoreConnectAuthHandlerTest.cls` | Apex test class covering valid tokens, expired tokens, tampered signatures, and invalid store IDs. |
| `force-app/main/default/lwc/storeConnectWrapper/` | Wrapper LWC that authenticates the token via Apex, then dynamically renders the requested component. Uses a component allowlist for security. |
| `force-app/main/default/aura/StoreConnectLWCOut/` | Lightning Out Aura application. This is a Salesforce requirement for serving LWC components externally. |
| `README.md` | Full setup instructions, architecture overview, and development commands. |

## Set up Salesforce

### Step 1: Configure the shared secret in Salesforce

Create a `Store_Variable__c` record for each store that will embed LWC components:

| Field | Value |
|-------|-------|
| `Store_Id__c` | Your StoreConnect Store ID |
| `Key__c` | `SC_HMAC_Secret` |
| `Value__c` | A strong, random secret string |
| `Available_In_Liquid__c` | `true` |

:::note
The secret must be identical on both the Salesforce and StoreConnect sides. Generate a strong random string (at least 32 characters) and keep it secure.
:::

### Step 2: Deploy the Apex and LWC components

From the project directory, deploy to your Salesforce org:

```bash
sf project deploy start --source-dir force-app
```

This deploys:
- The `StoreConnectAuthHandler` Apex class and its test class
- The `storeConnectWrapper` LWC
- The `StoreConnectLWCOut` Aura application

### Step 3: Set up an Experience Cloud site

If you do not already have an Experience Cloud site, create one in Salesforce Setup under **Digital Experiences > All Sites**. This site provides the Lightning Out endpoint URL that StoreConnect uses to load components.

Note the following URLs from your Experience Cloud site:
- **Lightning Out script URL** -- `https://{your-domain}.my.site.com/lightning/lightning.out.js`
- **Experience Cloud site URL** -- `https://{your-domain}.my.site.com`

## Set up StoreConnect

### Step 1: Configure Store Variables

In your StoreConnect admin, create three [Store Variables](store-variables):

| Store Variable | Description | Example value |
|----------------|-------------|---------------|
| `SC_HMAC_Secret` | The shared HMAC secret (must match the value in Salesforce) | A strong random string (at least 32 characters) |
| `SF_My_Salesforce` | Lightning Out script URL from your Experience Cloud site | `https://yourdomain.my.site.com/lightning/lightning.out.js` |
| `SF_My_Experience` | Your Experience Cloud site URL | `https://yourdomain.my.site.com` |

### Step 2: Add the Liquid template to your theme

Copy `templates/blocks/lwc.liquid` from the project into your StoreConnect theme's templates directory at `templates/blocks/lwc.liquid`.

This creates a new content block template called `lwc` that you can use to embed components.

### Step 3: Create a content block template option

In Salesforce, add `lwc` as a new picklist value to the `Content_Block__c.Template__c` field. This makes the template available when creating content blocks.

### Step 4: Embed a component

Create a content block in StoreConnect and:

1. Set the **Template** to `lwc`
2. Set the **Content** field to the name of the component you want to render (for example, `contactSupportForm`)

The Liquid template reads the component name from the content block, generates a signed token, and handles the Lightning Out initialization automatically.

## Register custom components

The wrapper uses a component allowlist for security. Only components explicitly imported and registered in the `COMPONENT_MAP` can be loaded.

To add a new embeddable component:

1. Create your LWC under `force-app/main/default/lwc/` in your Salesforce org.
2. Import it in `storeConnectWrapper.js` and add it to the `COMPONENT_MAP`:

```javascript
import MyComponent from 'c/myComponent';

const COMPONENT_MAP = {
    // ...existing components
    'myComponent': MyComponent,
};
```

3. Your component receives `authData` as a property containing all token claims:

```javascript
// In your custom component
import { LightningElement, api } from 'lwc';

export default class MyComponent extends LightningElement {
    @api authData; // Contains: isValid, customerId, and any custom claims
}
```

4. Deploy the updated wrapper and your new component to your Salesforce org.
5. Reference the component name in a StoreConnect content block using the `lwc` template.

:::tip
You can embed multiple LWC components on the same page. Each content block gets its own container, token, and event scope.
:::

## Token structure

The token generated by the Liquid template follows this format:

```
{storeId}|v1.{base64-encoded-payload}.{hex-hmac-signature}
```

The payload contains standard claims:

| Claim | Description |
|-------|-------------|
| `aud` | Store domain (audience) |
| `exp` | Expiry timestamp (1 hour from generation) |
| `iat` | Issued-at timestamp |
| `jti` | Nonce (store + customer + time + block ID for uniqueness) |
| `sub` | Customer ID, or `guest` for unauthenticated visitors |

## Auto-resizing

The wrapper LWC uses a `ResizeObserver` to monitor the rendered component's height and sends `postMessage` events back to the storefront. The Liquid template listens for these events and adjusts the container height automatically. This means embedded components resize smoothly without scrollbars or fixed heights.

Error events (`SC_AUTH_ERROR`) are also sent via `postMessage` and logged to the browser console, scoped to the specific component name.

## Troubleshooting

### Component does not render

- Verify the component name in the content block exactly matches a key in the `COMPONENT_MAP` in `storeConnectWrapper.js`
- Check the browser console for authentication errors
- Confirm the Experience Cloud site is published and accessible

### Authentication errors

- Confirm the `SC_HMAC_Secret` value is identical in both StoreConnect Store Variables and the Salesforce `Store_Variable__c` record
- Check that the `Store_Id__c` on the Salesforce record matches your StoreConnect Store ID
- If you see "Token expired" errors, verify that your server clocks are reasonably in sync (the system allows 30 seconds of skew)

### Lightning Out script fails to load

- Verify the `SF_My_Salesforce` Store Variable contains the correct URL ending in `/lightning/lightning.out.js`
- Confirm the Experience Cloud site is published and the URL in `SF_My_Experience` is correct
- Check for Content Security Policy (CSP) errors in the browser console -- you may need to add your Salesforce domain to your store's CSP trusted sites

### Multiple components on the same page

Each content block generates its own unique container ID and token. The `postMessage` events include the component name so resize and error events are scoped correctly. If components are interfering with each other, check that each content block has a unique identifier.