Authentication & Profile
Sesamy JS provides comprehensive authentication and profile management with support for multiple login flows and user data management.
Authentication
The SDK provides comprehensive authentication methods with support for both redirect and popup-based flows.
login()
Smart login that automatically chooses the best authentication method based on browser context.
Parameters:
options(optional):appState: Any JSON-serializable data to preserve during authentication (e.g.,{ source: 'registration-wall', articleId: '123' })authorizationParams:audience: API audiencescope: OAuth scopeslogin_hint: Pre-fill emailorganization: Auth0 organizationredirect_uri: Post-login redirect URL
Returns: Promise<void>
Example:
await window.sesamy.auth.login({
authorizationParams: {
login_hint: 'user@example.com',
},
});Example with metadata tracking:
// Track login source for analytics
await window.sesamy.auth.login({
appState: {
source: 'registration-wall',
articleId: 'article-123',
campaign: 'spring-2025',
},
});loginWithRedirect()
Redirects user to Auth0 hosted login page. Best for desktop browsers.
Parameters:
- Same as
login()
Returns: Promise<void>
Example:
await window.sesamy.auth.loginWithRedirect({
authorizationParams: {
redirect_uri: window.location.origin + '/callback',
},
});loginWithPopup()
Opens login in a popup window. Better for maintaining page state and works better in incognito mode.
Note: To prevent automatic page refresh after authentication, listen for sesamyJsAuthenticated event and call preventDefault().
Parameters:
- Same as
login()
Returns: Promise<void>
Example:
// Prevent auto-refresh after login
window.addEventListener('sesamyJsAuthenticated', (event) => {
event.preventDefault();
console.log('User authenticated!');
});
await window.sesamy.auth.loginWithPopup({
authorizationParams: {
login_hint: 'user@example.com',
},
});logout()
Logs out the user and optionally redirects.
Parameters:
options(optional):returnTo: URL to redirect after logout
Returns: Promise<void>
Example:
await window.sesamy.auth.logout({
returnTo: 'https://yoursite.com',
});isAuthenticated()
Checks if user is currently authenticated.
Returns: Promise<boolean>
Example:
const isAuth = await window.sesamy.auth.isAuthenticated();
if (isAuth) {
console.log('User is logged in');
}getTokenSilently()
Retrieves access token without user interaction.
Parameters:
throwOnUnauthorized(boolean, default: true): Throw error if not authenticatedforceRefresh(boolean, default: false): Force token refresh
Returns: Promise<string | null>
Example:
const token = await window.sesamy.auth.getTokenSilently();
// Use token for API callssetToken()
Manually set an access token.
Parameters:
accessToken(string): The access tokenexpiresIn(number, optional): Token expiration in seconds
Returns: Promise<void>
Example:
await window.sesamy.auth.setToken('your.jwt.token', 3600);Profile Management
profile.get()
Fetches the user's profile information.
Returns: Promise<Profile | null>
Profile Type:
type Profile = {
userId: string;
firstName?: string;
lastName?: string;
emailVerified: boolean;
email: string;
name?: string;
locale?: string;
picture?: string;
createdAt: string;
updatedAt: string;
mobilePhone?: string;
tags: string[];
user_metadata?: Record<string, string | number>;
billingAddress?: Address;
deliveryAddress?: Address;
};Example:
const profile = await window.sesamy.profile.get();
if (profile) {
console.log('User email:', profile.email);
console.log('Name:', profile.firstName, profile.lastName);
console.log('Email verified:', profile.emailVerified);
}profile.update()
Updates the user's profile information.
Parameters:
profile(Partial<Profile>): Fields to update
Returns: Promise<boolean>
Example:
const success = await window.sesamy.profile.update({
firstName: 'John',
lastName: 'Doe',
mobilePhone: '+1234567890',
billingAddress: {
street: '123 Main St',
city: 'New York',
state: 'NY',
postalCode: '10001',
country: 'US',
},
});
if (success) {
console.log('Profile updated successfully');
}profile.openHostedAccountPage()
Opens the Sesamy hosted account management page where users can manage their subscriptions, payment methods, and personal information.
Example:
// Add to a "My Account" button
document.querySelector('#account-btn').addEventListener('click', () => {
window.sesamy.profile.openHostedAccountPage();
});profile.isSpotifyLinked()
Checks if the user has linked their Spotify account.
Returns: Promise<boolean>
Example:
const isLinked = await window.sesamy.profile.isSpotifyLinked();
if (isLinked) {
console.log('Spotify account is linked');
}profile.unlinkSpotify()
Unlinks the user's Spotify account.
Returns: Promise<void>
Example:
await window.sesamy.profile.unlinkSpotify();
console.log('Spotify account unlinked');Authentication Modes
Sesamy JS supports two authentication backends. Choose the one that fits your infrastructure.
BFF (Cookie-based) Auth — recommended for new integrations
The BFF (Backend for Frontend) pattern stores tokens in HttpOnly cookies managed by the Sesamy API proxy. JavaScript never has access to raw access tokens, providing stronger XSS protection and eliminating token storage in localStorage.
Requirements:
- The
/auth/*routes must be served from the same origin as your page (so the browser sends cookies automatically). You can achieve this either with a relative proxy path or a custom domain — see the options below. - The Token Handler capability enabled for your vendor (contact Sesamy to set this up)
Option A — Relative path (proxy on same origin)
The simplest setup: your backend (Next.js, Vite dev server, nginx, etc.) proxies /auth/* and API calls to the Sesamy API proxy. No custom domain required.
{
"clientId": "your-vendor-id",
"api": {
"endpoint": "/api"
},
"auth": {
"useHttpCookies": true
}
}All requests (/api/entitlements, /auth/login, etc.) go to the same origin as your page — cookies are first-party automatically.
Option B — Custom API domain
Use a dedicated subdomain (e.g. api.yoursite.com) pointing at the Sesamy API proxy. The page and the API domain must share a top-level domain so the cookie is first-party.
{
"clientId": "your-vendor-id",
"api": {
"endpoint": "https://api.yoursite.com"
},
"auth": {
"useHttpCookies": true
}
}With this setup, all auth flows (login, logout, token refresh, session check) route through /auth/* on your API domain. The session cookie is set as HttpOnly; SameSite=Lax by the server — JS never reads it directly.
BFF endpoints used by sesamy-js:
| Endpoint | Description |
|---|---|
GET /auth/:vendorId/login | Redirects user to the auth provider login page |
GET /auth/:vendorId/callback | Handles the OAuth callback and sets the session cookie |
GET /auth/logout | Clears the session cookie and optionally redirects to the auth provider |
GET /auth/userinfo | Returns auth status and user info (no token exposure) |
Auth0 SPA JS Plugin — for existing integrations
For publishers already using the Auth0 redirect/popup flow, the auth0-plugin preserves the existing behaviour. Tokens are managed by @auth0/auth0-spa-js in session/localStorage.
The plugin ships as a separate entry point. Pass it directly to init() via the authPlugin option:
Via npm (recommended):
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',
},
},
{ authPlugin: createAuth0Plugin() },
);Via script tags (no build tool):
Load the auth0-plugin before sesamy-js. When the sesamy-js bundle initialises from the <script type="application/json"> element it automatically detects the auth0Plugin global exposed by the IIFE and passes it to init():
<!-- auth0-plugin IIFE must be loaded first -->
<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>Legacy: registerAuthPlugin()
In older versions of sesamy-js (< 1.95.0) the plugin had to be registered globally before init() ran:
import { registerAuthPlugin, init } from '@sesamy/sesamy-js';
import { createAuth0Plugin } from '@sesamy/sesamy-js/auth0-plugin';
// Must be called before init()
registerAuthPlugin(createAuth0Plugin());
const sesamy = await init({ clientId: 'your-client-id' });This still works but the authPlugin option is preferred — it's explicit and doesn't rely on shared global state.
Scripts Host: automatic mode selection
If you use the Scripts Host service, the auth mode is selected automatically based on your vendor configuration — no code changes required:
| Vendor config | Auth mode |
|---|---|
No apiDomain set | Auth0 plugin is prepended to the bundle. All existing clients continue to work unchanged. |
apiDomain set (e.g. api.yoursite.com) | Cookie (BFF) auth is enabled; the Auth0 plugin is omitted from the bundle. |
Switching modes via Scripts Host is transparent: updating the apiDomain field in your Scripts Host vendor configuration is all that is needed.
Cross-Domain Authentication
For applications spanning multiple domains (e.g., white-label sites, multi-region deployments), Sesamy JS supports automatic domain selection:
{
auth: {
clientId: "your-client-id",
domains: [
"auth.brand1.com",
"auth.brand2.com",
"auth.example.co.uk"
],
domain: "default-auth.example.com" // Fallback domain
}
}The SDK automatically selects the appropriate auth domain based on the current page's top-level domain, ensuring seamless authentication across your domain portfolio.
Common Patterns
Protected Content
window.addEventListener('sesamyJsReady', async () => {
const isAuth = await window.sesamy.auth.isAuthenticated();
if (isAuth) {
const profile = await window.sesamy.profile.get();
document.querySelector('.user-name').textContent = profile.name;
document.querySelector('.premium-content').style.display = 'block';
} else {
document.querySelector('.login-prompt').style.display = 'block';
}
});Login/Logout Button
const authBtn = document.querySelector('#auth-btn');
window.addEventListener('sesamyJsReady', async () => {
const updateAuthButton = async () => {
const isAuth = await window.sesamy.auth.isAuthenticated();
if (isAuth) {
const profile = await window.sesamy.profile.get();
authBtn.textContent = `Logout (${profile.email})`;
authBtn.onclick = () => window.sesamy.auth.logout();
} else {
authBtn.textContent = 'Login';
authBtn.onclick = () => window.sesamy.auth.login();
}
};
await updateAuthButton();
// Update button after authentication changes
window.addEventListener('sesamyJsAuthenticated', updateAuthButton);
window.addEventListener('sesamyJsLoggedOut', updateAuthButton);
});Profile Form
window.addEventListener('sesamyJsReady', async () => {
const profile = await window.sesamy.profile.get();
if (profile) {
// Pre-fill form
document.querySelector('#firstName').value = profile.firstName || '';
document.querySelector('#lastName').value = profile.lastName || '';
document.querySelector('#email').value = profile.email;
}
// Handle form submission
document.querySelector('#profileForm').addEventListener('submit', async (e) => {
e.preventDefault();
const success = await window.sesamy.profile.update({
firstName: document.querySelector('#firstName').value,
lastName: document.querySelector('#lastName').value,
mobilePhone: document.querySelector('#phone').value,
});
if (success) {
alert('Profile updated successfully!');
}
});
});Login Paywall with Tracking
Track where users are logging in from using appState:
// Custom login paywall component
class LoginPaywall extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<div class="paywall">
<h2>Subscribe to continue reading</h2>
<button id="paywall-login">Sign up or Login</button>
</div>
`;
this.querySelector('#paywall-login').addEventListener('click', () => {
window.sesamy.auth.login({
appState: {
source: 'registration-wall',
articleId: this.getAttribute('article-id'),
contentType: this.getAttribute('content-type') || 'article',
},
});
});
}
}
customElements.define('login-paywall', LoginPaywall);
// Listen for authentication with metadata
window.addEventListener('sesamyJsAuthenticated', (event) => {
const { appState, email, sub } = event.detail;
if (appState?.source === 'registration-wall') {
// Track conversion
console.log('User registered from paywall', {
source: appState.source,
articleId: appState.articleId,
contentType: appState.contentType,
userId: sub,
});
// Send to analytics
if (window.gtag) {
window.gtag('event', 'registration_completed', {
source: appState.source,
article_id: appState.articleId,
});
}
}
});Usage in HTML:
<login-paywall article-id="spring-news-2025" content-type="premium-article"></login-paywall>Next Steps
- API Reference - Check entitlements and manage subscriptions
- Content Management - Implement paywalls and access control
- Analytics - Track user interactions