Skip to content

CMS Integrations

An overview of the building blocks for integrating Sesamy into your CMS. This covers authentication strategies, content protection methods, and Sesamy's web components that the platform-specific guides build upon.

Authentication

Sesamy supports three authentication approaches. The right choice depends on your infrastructure.

The BFF (Backend for Frontend) pattern stores tokens in HttpOnly cookies that JavaScript can never read, eliminating the XSS token-theft surface entirely. Your backend proxies /auth/* and /api/* to the Sesamy API so that cookies are first-party.

text
Browser            Your Server / CDN Edge         Sesamy API
  |                        |                          |
  |-- GET /auth/login ---> |-- proxy --------------> |
  |<- Set-Cookie --------- |<----------------------- |
  |                        |                          |
  |-- GET /api/entitlements |-- proxy --------------> |
  |   (cookie attached)     |                          |
  |<- data --------------- |<----------------------- |

Setup: Configure your server (nginx, Vercel, Cloudflare, etc.) to proxy two paths:

PathDestination
/auth/*https://api.sesamy.com/auth/*
/api/*https://api.sesamy.com/*

Then configure sesamy-js to use relative paths and cookie auth:

json
{
  "clientId": "your-vendor-id",
  "vendorId": "your-vendor-id",
  "api": {
    "endpoint": "/api"
  },
  "auth": {
    "useHttpCookies": true
  }
}

Since all requests go to your own origin, cookies are first-party automatically. No custom domain or CNAME required.

Example: nginx

nginx
location /auth/ {
    proxy_pass https://api.sesamy.com/auth/;
    proxy_set_header Host api.sesamy.com;
    proxy_set_header X-Forwarded-Host $host;
}
location /api/ {
    proxy_pass https://api.sesamy.com/;
    proxy_set_header Host api.sesamy.com;
    proxy_set_header X-Forwarded-Host $host;
}

Example: Vercel / Next.js

typescript
// next.config.ts
const nextConfig = {
  async rewrites() {
    return [
      { source: '/auth/:path*', destination: 'https://api.sesamy.com/auth/:path*' },
      { source: '/api/:path*', destination: 'https://api.sesamy.com/:path*' },
    ];
  },
};

Vercel runs rewrites at the Edge Network, so there is no cold-start overhead.

Option 2: BFF with Custom API Domain

If you cannot set up a reverse proxy, point a subdomain (e.g. api.yoursite.com) at the Sesamy API proxy via CNAME. The page and the API domain must share a top-level domain so the cookie is first-party.

text
DNS:  api.yoursite.com  CNAME  api.sesamy.com
json
{
  "clientId": "your-vendor-id",
  "vendorId": "your-vendor-id",
  "api": {
    "endpoint": "https://api.yoursite.com"
  },
  "auth": {
    "useHttpCookies": true
  }
}

The session cookie is set as HttpOnly; SameSite=Lax by the server. Contact Sesamy to enable the Token Handler for your custom domain.

Option 3: SPA Auth (Auth0 Plugin)

For publishers who already use Auth0 or need popup/redirect login flows. Tokens are managed by @auth0/auth0-spa-js in the browser. This is simpler to set up but stores tokens in localStorage, which is accessible to any JavaScript on the page.

Via Scripts Host (no code change needed):

When your vendor has no custom apiDomain configured, the Scripts Host bundle automatically includes the Auth0 plugin. This is the default for existing integrations.

Via npm:

typescript
import { init } from '@sesamy/sesamy-js';
import { createAuth0Plugin } from '@sesamy/sesamy-js/auth0-plugin';

const sesamy = await init(
  {
    clientId: 'your-client-id',
    auth: {
      domain: 'login.yoursite.com',   // your Auth0 custom domain
    },
  },
  { authPlugin: createAuth0Plugin() },
);

Via script tags:

html
<script src="https://cdn.sesamy.com/sesamy-js/latest/auth0-plugin.iife.js"></script>
<script src="https://cdn.sesamy.com/sesamy-js/latest/sesamy-js.iife.js"></script>
<script type="application/json" id="sesamy-js">
  {
    "clientId": "your-client-id",
    "auth": { "domain": "login.yoursite.com" }
  }
</script>

Which auth mode should I use?

CriteriaBFF (Proxy)BFF (Custom Domain)SPA (Auth0)
SecurityBest -- tokens never in JSBest -- tokens never in JSGood -- tokens in localStorage
Setup complexityMedium -- requires proxy configLow -- DNS CNAME onlyLowest -- no infra changes
Custom domain requiredNoYesOptional (for Auth0)
Third-party cookie issuesNoneNonePossible in Safari/Firefox
Recommended forNew integrationsSites without proxy capabilityExisting Auth0 integrations

Content Protection

Sesamy offers several strategies for protecting premium content. They differ in security level and implementation complexity.

Capsule uses AES-256-GCM encryption to protect content at rest. The encrypted content is embedded directly in the HTML -- even if someone views the page source, they see only ciphertext. Keys are fetched from the server only after entitlement is verified.

html
<!-- DCA data embedded by your CMS/build tool -->
<script class="dca-data" type="application/json">
  {
    "version": "1",
    "resource": { "resourceId": "article-123", ... },
    "resourceJWT": "eyJ...",
    "issuerData": {
      "sesamy": {
        "unlockUrl": "https://api.yoursite.com/capsule/vendors/your-vendor/dca/unlock",
        "sealed": { ... },
        "keyId": "premium"
      }
    },
    ...
  }
</script>
<template class="dca-sealed-content">
  <div data-dca-content-name="bodytext">U2FsdGVkX1...encrypted...</div>
</template>

Enable capsule in your sesamy-js config:

json
{
  "clientId": "your-vendor-id",
  "vendorId": "your-vendor-id",
  "capsule": {
    "enabled": true
  }
}

sesamy-js will automatically:

  1. Detect the <script class="dca-data"> element on the page
  2. Authenticate the user and call the unlock endpoint
  3. Decrypt all content items
  4. Inject the decrypted HTML into the page

Why Capsule?

  • Content is encrypted at rest -- viewing page source reveals nothing
  • No server-side content gating needed -- the HTML is always the same
  • Works with static sites and CDNs -- no dynamic rendering required
  • Share links supported via pre-signed tokens
  • Cache-friendly -- encrypted pages can be cached like any other page

Strategy 2: Proxy Lock Mode

The content is stored on the server and fetched only after the user is authenticated and entitled. The page initially contains only a preview; the full content is never in the HTML source.

html
<sesamy-content-container
  item-src="https://example.com/articles/premium-article"
  publisher-content-id="article-123"
  lock-mode="proxy"
>
  <div slot="preview">
    <p>This is a preview of the premium content...</p>
  </div>
</sesamy-content-container>

Good security, but requires the content to be available via URL for the proxy to fetch. Not suitable for fully static sites where content is baked into the HTML at build time.

Strategy 3: Encode Lock Mode

Content is base64-encoded in the page source and decoded client-side after authentication. Prevents casual copy-paste but is not cryptographically secure -- anyone who reads the page source can decode the content.

html
<sesamy-content-container
  item-src="https://example.com/articles/encoded-article"
  publisher-content-id="article-456"
  lock-mode="encode"
>
  <div slot="preview">
    <p>Free preview...</p>
  </div>
</sesamy-content-container>

Strategy 4: Embed Lock Mode (CSS Hidden)

Content is present in the DOM but hidden with CSS until the user is authenticated. The least secure option -- the content is fully visible in the page source.

html
<sesamy-content-container
  item-src="https://example.com/articles/embedded-article"
  publisher-content-id="article-789"
  lock-mode="embed"
>
  <div slot="preview">
    <p>Read the first paragraph for free...</p>
  </div>

  <div slot="locked">
    <p>This content is hidden with CSS until unlocked.</p>
  </div>
</sesamy-content-container>

Strategy 5: Event Lock Mode

No content is managed by Sesamy. Instead, a sesamyUnlocked event fires when the user has access, and you handle the content display yourself.

html
<sesamy-content-container
  item-src="https://example.com/articles/custom-article"
  publisher-content-id="event-101"
  lock-mode="event"
>
  <div slot="preview">
    <p>Subscribe to unlock this content.</p>
  </div>
</sesamy-content-container>

<script>
  document.addEventListener('sesamyUnlocked', (event) => {
    const { publisherContentId } = event.detail;
    // Fetch and display content however you like
    fetchAndRenderContent(publisherContentId);
  });
</script>

Strategy 6: Fetch from API

For fully custom implementations, skip the web components and use the sesamy-js API directly to check entitlements and gate content in your own code.

javascript
window.addEventListener('sesamyJsReady', async () => {
  const hasAccess = await window.sesamy.entitlements.hasAccess('premium-article-123');

  if (hasAccess) {
    document.querySelector('.premium-content').style.display = 'block';
  } else {
    document.querySelector('.paywall').style.display = 'block';
  }
});

Comparison

StrategySecurityContent in source?CDN-friendlyComplexity
Capsule (DCA)ExcellentEncrypted onlyYesLow (auto-processed)
ProxyGoodNoNo (dynamic fetch)Low
EncodeLowEncoded (reversible)YesLow
Embed (CSS)NoneYes (plain text)YesLowest
EventDepends on impl.NoDependsMedium
API fetchDepends on impl.NoDependsHighest

Web Components

Sesamy provides web components that handle authentication, paywall display, and content gating. They work with any HTML page or framework.

Loading the Components

The simplest way is via the Scripts Host, which bundles sesamy-js, the auth plugin, and the web components:

html
<script type="module" src="https://scripts.sesamy.com/s/YOUR_VENDOR_ID/bundle/stable"></script>

sesamy-login

Renders a login/logout button with user avatar. Handles the full auth flow automatically.

html
<header>
  <nav>
    <a href="/">Home</a>
    <sesamy-login></sesamy-login>
  </nav>
</header>

Style it with CSS custom properties:

css
sesamy-login {
  --sesamy-font-family: 'Inter', sans-serif;
  --sesamy-login-button-background: #1a1a1a;
  --sesamy-login-button-color: white;
  --sesamy-login-button-border-radius: 8px;
}

See sesamy-login reference for all properties, events, and slots.

sesamy-paywall

Displays pricing options and purchase/subscribe buttons. Fetches its configuration from a settings-url endpoint managed via the Sesamy dashboard.

html
<sesamy-paywall
  settings-url="https://api2.sesamy.com/paywalls/YOUR_VENDOR_ID/YOUR_PAYWALL_ID"
>
</sesamy-paywall>

You can customize the features section with a slot:

html
<sesamy-paywall
  settings-url="https://api2.sesamy.com/paywalls/YOUR_VENDOR_ID/YOUR_PAYWALL_ID"
  price="4.99"
  currency="EUR"
>
  <div slot="features">
    <ul>
      <li>Unlimited access to all articles</li>
      <li>Ad-free reading experience</li>
      <li>Cancel anytime</li>
    </ul>
  </div>
</sesamy-paywall>

Paywall without content lock

You can use <sesamy-paywall> on its own for subscription pages or donation pages -- it doesn't require a <sesamy-content-container>.

See sesamy-paywall reference for all properties, events, and slots.

sesamy-content-container

Manages content visibility based on the user's entitlements. Pair it with a <sesamy-paywall> for the full experience.

html
<article
  publisher-content-id="article-123"
  item-src="https://example.com/articles/premium-article"
>
  <sesamy-paywall
    settings-url="https://api2.sesamy.com/paywalls/YOUR_VENDOR_ID/YOUR_PAYWALL_ID"
  ></sesamy-paywall>

  <sesamy-content-container lock-mode="proxy">
    <div slot="preview">
      <p>This preview is visible to everyone...</p>
    </div>
  </sesamy-content-container>
</article>

Key attributes:

AttributeDescription
lock-modeHow content is protected: proxy, embed, encode, event
access-levelWho can access: public, logged-in, entitlement
item-srcURL identifying the content
publisher-content-idYour internal content ID
passSemicolon-separated pass IDs for tier-based access

See sesamy-content-container reference for the full API.

sesamy-visibility

A simple component for showing/hiding content based on authentication status. No entitlement check -- just logged in or not.

html
<sesamy-visibility>
  <div slot="logged-in">
    <p>Welcome back! <a href="#" onclick="window.sesamy.profile.openHostedAccountPage()">Manage account</a></p>
  </div>
  <div slot="not-logged-in">
    <p>Log in to access your subscriptions.</p>
  </div>
</sesamy-visibility>

How Components Find Configuration

Components traverse the DOM upward to find configuration attributes. This means you can set shared properties on a parent element (typically an <article> tag) and all child components inherit them:

html
<article
  publisher-content-id="article-123"
  item-src="https://example.com/articles/premium-article"
  price="4.99"
  currency="EUR"
>
  <!-- Both components inherit publisher-content-id, item-src, price, currency -->
  <sesamy-paywall settings-url="..."></sesamy-paywall>
  <sesamy-content-container lock-mode="proxy">
    <div slot="preview">Preview...</div>
  </sesamy-content-container>
</article>

Putting It All Together

Here is a complete example combining BFF auth, Capsule encryption, and Sesamy components:

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Premium Article</title>

    <!-- Load Sesamy bundle (includes components + auth) -->
    <script type="module" src="https://scripts.sesamy.com/s/YOUR_VENDOR_ID/bundle/stable"></script>

    <!-- Override sesamy-js config for BFF auth + Capsule -->
    <script type="application/json" id="sesamy-js">
      {
        "clientId": "your-vendor-id",
        "vendorId": "your-vendor-id",
        "api": { "endpoint": "/api" },
        "auth": { "useHttpCookies": true },
        "capsule": { "enabled": true }
      }
    </script>
  </head>
  <body>
    <header>
      <h1>My Publication</h1>
      <sesamy-login></sesamy-login>
    </header>

    <article
      publisher-content-id="article-2025-001"
      item-src="https://example.com/articles/premium-article"
    >
      <h1>Premium Article Title</h1>
      <p>This introduction is visible to everyone.</p>

      <!-- Paywall: shown when user lacks access -->
      <sesamy-paywall
        settings-url="https://api2.sesamy.com/paywalls/YOUR_VENDOR_ID/YOUR_PAYWALL_ID"
      ></sesamy-paywall>

      <!-- Encrypted content: decrypted automatically by Capsule -->
      <script class="dca-data" type="application/json">
        { ... }
      </script>
      <template class="dca-sealed-content">
        <div data-dca-content-name="bodytext">...encrypted...</div>
      </template>
    </article>

    <sesamy-visibility>
      <div slot="logged-in">
        <a href="#" onclick="window.sesamy.profile.openHostedAccountPage()">My Account</a>
      </div>
    </sesamy-visibility>
  </body>
</html>

Released under the MIT License.