Skip to main content

Overview

The OFAuth Link Embed library provides pre-built components and a seamless popup flow for hosted link account authentication. Instead of redirecting users away from your application, the embed library allows users to authenticate within your site using popups, iframes, or web components.
Best User Experience: The Embed library provides the most seamless authentication experience for your users while maintaining the security benefits of hosted mode.

Features

  • Popup Integration: Open authentication in a popup window without leaving your application.
  • Multiple Implementation Options: JavaScript library, global script, or web component.
  • Theme Support: Light and dark themes to match your application.
  • Async Event Handling: Rich event system for handling success, close, and invalid session events, with support for async callbacks.
  • Automatic Session Renewal: Built-in onInvalidSession callback to handle expired sessions gracefully.
  • TypeScript Support: Full TypeScript definitions for a better development experience.
  • Framework Agnostic: Works with React, Vue, Svelte, vanilla JavaScript, and more.

Security: Allowed Origins & CSP

OFAuth enforces where your Link iframe may be embedded using Content Security Policy (CSP) frame-ancestors.

Allowed Origins

Configure trusted web origins in your OFAuth dashboard. Only these origins can embed the hosted Link page.

CSP Enforcement

The hosted Link page sets Content-Security-Policy: frame-ancestors 'self' <your-origins...>. Browsers block embedding from unlisted origins.
For local development you can add your dev origin (e.g. http://localhost:5173) to the list. In production, use HTTPS origins only.

Configuration Rules

  • Provide full origins, e.g. https://app.example.com (scheme + host, optional port)
  • Wildcards are not supported; list each origin explicitly
  • An empty list means only 'self' (no third‑party iframes)
  • Changes apply to newly loaded sessions immediately

Troubleshooting

  • If the embed fails to appear or the browser console shows a CSP error mentioning frame-ancestors, add your site’s origin to Allowed Origins
  • If you see a “Link is misconfigured” screen, verify your dashboard’s Allowed Origins and Allowed Redirect URIs (see Hosted guide)

Installation

NPM Package

npm install @ofauth/link-embed

CDN (Global Script)

<script
	src="https://unpkg.com/@ofauth/link-embed/dist/embed.global.js"
	defer
	data-auto-init
></script>

Implementation Options

The embed library offers three implementation approaches to fit different use cases: The JavaScript library provides the most control and is perfect for modern web applications.

Basic Usage

import { OFAuthLinkEmbed } from '@ofauth/link-embed';

// 1. Create a handler (URL is not required at creation)
const handler = OFAuthLinkEmbed.create({
	theme: 'auto',
	async onSuccess(metadata) {
		console.log('Authentication successful:', metadata);
		// You can perform async actions here before the embed closes
		await fetch('/api/notify-success', {
			method: 'POST',
			body: JSON.stringify({ user: metadata.connection.userData.id })
		});
	},
	onClose(metadata) {
		console.log('Authentication closed:', metadata);
	},
	async onInvalidSession() {
        console.log("Session invalid, creating a new one...");
        const response = await fetch("/api/create-link-session");
        const { url } = await response.json();
        handler.open(url);
    }
});

// 2. Open the authentication popup with a session URL from your backend
async function startAuth() {
    const response = await fetch("/api/create-link-session");
    const { url } = await response.json();
	handler.open(url);
}

// Call startAuth() when you're ready, e.g., on a button click
// startAuth();

Handler API

The create() method returns a handler object with the following interface:
interface LinkHandler {
	open(url?: string): void;               // Opens the authentication popup. Can be called with a new URL.
	close(options?: { force?: boolean }): void; // Closes and destroys the authentication popup.
	setUrl(url: string): void;              // Sets or updates the URL for the next time `open()` is called.
	cleanup(): void;                         // Optional, used to cleanup html elements manually. Called automatically when embed has closed.
}

Configuration Options

OptionTypeRequiredDescription
urlstringOptional default Link session URL. Can be passed to open().
theme'light' | 'dark' | 'auto'Theme for the authentication interface (default: ‘auto’).
onSuccess(metadata: SuccessMetadata) => voidCallback when authentication succeeds. Can be async.
onClose(metadata: CloseMetadata) => voidCallback when user closes the embed. Can be async.
onLoad() => voidCallback when the iframe is fully loaded. Can be async.
onInvalidSession() => voidCallback when the session is invalid. Can be async.

Global Script Integration

For quick integration without a build process, use the global script approach with HTML data attributes.

Basic Usage

<!-- Authentication trigger button -->
<a
	data-ofauth-link
	href="https://link.ofauth.com/cs_xxxxxxxxx"
	data-ofauth-theme="auto"
	class="auth-button"
>
	Connect OnlyFans Account
</a>

<!-- Include the embed script -->
<script
	src="https://unpkg.com/@ofauth/link-embed/dist/embed.global.js"
	defer
	data-auto-init
></script>

Event Handling

With the global script, you can listen for DOM events on the trigger element.
const authButton = document.querySelector('[data-ofauth-link]');

// Listen for authentication events
authButton.addEventListener('success', (event) => {
	const metadata = event.detail.metadata;
	console.log('Authentication successful:', metadata);
});

authButton.addEventListener('close', (event) => {
	const metadata = event.detail.metadata;
	console.log('Authentication closed:', metadata);
});

authButton.addEventListener('invalid_session', (event) => {
	console.log('The link session is invalid or has expired.');
    // You might want to refresh the page or get a new link
});

Global Script Attributes

AttributeDescriptionRequired
data-ofauth-linkMarks the element as an authentication trigger
hrefThe Link client session URL
data-ofauth-themeTheme: ‘light’, ‘dark’, or ‘auto’ (default: ‘auto’)

Web Component

Use the embed as a native web component in any framework or vanilla HTML.

Basic Usage

// Import the component
import '@ofauth/link-embed/component';
<ofauth-link
	url="https://link.ofauth.com/cs_xxxxxxxxx"
	theme="auto"
	label="Connect OnlyFans Account"
></ofauth-link>

Event Handling

const authComponent = document.querySelector('ofauth-link');

authComponent.addEventListener('success', (event) => {
	const metadata = event.detail.metadata;
	console.log('Authentication successful:', metadata);
});

authComponent.addEventListener('close', (event) => {
	const metadata = event.detail.metadata;
	console.log('Authentication closed:', metadata);
});

authComponent.addEventListener('invalid_session', (event) => {
	console.log('The link session is invalid or has expired.');
    // You might want to get a new link and update the component's url property
});

Web Component Attributes

AttributeTypeRequiredDescription
urlstringThe Link client session URL
theme'light' | 'dark' | 'auto'Theme (default: ‘auto’)
labelstringButton text (default: ‘Connect Account’)

Session Expiry & Renewal

Link sessions expire after 6 hours. The embed library makes it easy to handle this by listening for the onInvalidSession event when using the JavaScript library.
const handler = OFAuthLinkEmbed.create({
	async onInvalidSession() {
        console.log("Session is invalid or expired, creating a new one...");
        const response = await fetch("/api/create-link-session"); // Your backend endpoint
        const { url } = await response.json();
        
        // Re-open the embed with the new session URL
        handler.open(url);
    },
    onClose: (metadata) => {
        // This will still fire after an invalid session is handled
        console.log('Embed closed. Type:', metadata.type);
    }
});

// When user clicks "Connect"
async function start() {
    const response = await fetch("/api/create-link-session");
    const { url } = await response.json();
    handler.open(url);
}

Event Data Types

SuccessMetadata

interface SuccessMetadata {
	successUrl: string;     // /v2/link/init successUrl with {CONNECTION_ID} resolved
	redirect: boolean;      // if redirect should occur based on your configuration
	connection: {
		id: string;         // The connection ID to store and use with Access API
		userData: {
			id: string;
			name: string;
			username: string;
			avatar: string;
		};
	};
}
The onSuccess callback is where you receive the connection ID for embedded flows. Use this alongside the clientReferenceId you provided when initializing the Link session to store the connection for the correct user.
// Example: Store connection ID when authentication succeeds
const handler = OFAuthLinkEmbed.create({
	async onSuccess(metadata) {
		// metadata.connection.id is the connection ID
		await fetch('/api/store-connection', {
			method: 'POST',
			headers: { 'Content-Type': 'application/json' },
			body: JSON.stringify({
				connectionId: metadata.connection.id,
				userId: currentUserId // Your internal user ID
			})
		});
	}
});
If you supplied a success URL containing {CONNECTION_ID}, the success event receives the fully expanded URL, so you can redirect immediately or record the identifier without additional lookups.

CloseMetadata

interface CloseMetadata {
	type: 'user_exit' | 'error' | 'forced_exit';
	step?: 'pre-login' | 'login' | '2fa' | 'approval' | 'success' | 'error';
	error: {
		error_type: string;
		error_message: string;
	} | null;
}

Global Script Events

EventDetail TypeDescription
success{ metadata: SuccessMetadata }Fired when authentication succeeds
close{ metadata: CloseMetadata }Fired when user closes the embed
loadednullFired when the iframe is ready
invalid_sessionnullFired if the session is not valid

Styling and Themes

Built-in Themes

The embed library includes three built-in themes:
  • Light Theme: Clean, bright interface suitable for light backgrounds
  • Dark Theme: Modern dark interface for dark mode applications
  • Auto Theme: Automatically adapts to system preference

Custom Styling

The trigger elements can be styled with regular CSS. For web components, use CSS parts:
/* Style web component button */
ofauth-link::part(button) {
	background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
	color: white;
	border: none;
	padding: 14px 28px;
	border-radius: 8px;
	font-size: 16px;
	font-weight: 600;
	cursor: pointer;
	transition: transform 0.2s, box-shadow 0.2s;
}

ofauth-link::part(button):hover {
	transform: translateY(-2px);
	box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}

Error Handling

Common Scenarios

const handler = OFAuthLinkEmbed.create({
	url: authUrl,
	onSuccess: (metadata) => {
		console.log('Success:', metadata);
	},
	onClose: (metadata) => {
		if (metadata.error) {
			console.error('Authentication error:', metadata.error);
		} else {
			console.log('User cancelled at step:', metadata.step);
		}
	}
});

Session Expiry

Link sessions expire after 6 hours. Handle expired sessions gracefully:
const handler = OFAuthLinkEmbed.create({
	onInvalidSession: async () => {
		const url = await createLinkSession();
		handler.open(url);
	}
});

Browser Support

The Link Embed library supports all modern browsers:
  • Chrome/Edge: Latest versions
  • Firefox: Latest versions
  • Safari: Latest versions
  • Mobile Browsers: iOS Safari, Chrome Mobile
Internet Explorer: Not supported. The library uses modern web APIs that are not available in IE.

Performance Optimization

Preloading

The handler API provides better performance by pre-initializing the authentication iframe:
// The iframe is created and loaded when create() is called
const handler = OFAuthLinkEmbed.create({
	url: authUrl,
	// ... configuration
});

// Opening is instant since the iframe is already loaded
handler.open();

Resource Management

Always clean up handlers when they’re no longer needed:
// In React useEffect cleanup
useEffect(() => {
	const handler = createHandler();
	
	return () => {
		handler.destroy(); // Removes iframe and cleans up event listeners
	};
}, []);

Troubleshooting

Wait for the handler to be ready before opening:
const handler = OFAuthLinkEmbed.create(config);

if (!handler.ready) {
	// Wait for ready state or listen for load event
	config.onLoad = () => {
		handler.open();
	};
} else {
	handler.open();
}
The embed library handles CORS automatically. If you encounter issues, ensure:
  • Your domain is properly configured in your OFAuth dashboard
  • You’re using HTTPS in production
  • The session URL is valid and not expired

Next Steps