Embedding Lessons on External Websites
Learn how to embed Teacharium lessons on your website or application using secure iframe-based embedding with JWT authentication.
Overview
Teacharium provides a complete solution for embedding interactive lessons on external websites. The embedding system uses:
- JWT tokens for secure, time-limited access
- Iframe embedding for isolated, secure content delivery
- JavaScript SDK for easy integration
- PostMessage API for communication between the embedded lesson and parent page
Prerequisites
Before you begin, you’ll need:
- A Teacharium account with at least one published lesson
- Public API credentials (public key and secret key)
- A website or application where you want to embed lessons
Quick Start
Step 1: Install the SDK
Install the Teacharium Embed SDK in your project:
npm install @teacharium/embed-sdkOr include it directly via CDN:
<script src="https://unpkg.com/@teacharium/embed-sdk@latest/dist/teacharium-embed-sdk.umd.js"></script>Step 2: Generate a Token
On your backend server, generate a signed JWT token for the lesson:
const response = await fetch('https://www.teacharium.io/api/public/sign-token', {
method: 'POST',
headers: {
'Authorization': `Bearer ${publicKey}.${secretKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
lessonId: 'your-lesson-id',
learnerId: 'learner_abc123', // Required: anonymous identifier for the learner
userAttributes: {
userId: 'user_67890',
sessionId: 'session_abc123',
accountType: 'premium'
},
timeout: 7200 // 2 hours
})
});
const { token, expiresAt } = await response.json();Important Security Notes:
- Always generate tokens on your backend server. Never expose your API secret key in client-side code.
- Do not include PII (Personally Identifiable Information) such as email addresses, full names, phone numbers, or addresses in
userAttributes. Use anonymous identifiers like user IDs or session IDs instead. This protects user privacy and complies with data protection regulations.
Step 3: Embed the Lesson
Add a container element to your HTML:
<div id="lesson-container"></div>Then embed the lesson using the SDK:
import { embedLesson } from '@teacharium/embed-sdk';
const embed = embedLesson({
container: '#lesson-container',
token: token, // Token from Step 2
// baseUrl defaults to https://www.teacharium.io
// lessonId is automatically extracted from the token
width: '100%',
height: '600px',
onLoad: () => console.log('Lesson loaded'),
onComplete: (data) => console.log('Lesson completed', data),
onProgress: (data) => console.log('Progress update', data),
onError: (error) => console.error('Error', error)
});Note: The SDK automatically uses https://www.teacharium.io as the base URL and extracts the lessonId from the token, so you typically don’t need to specify these values unless you’re using a self-hosted instance.
Detailed Guide
Token Generation
Tokens are generated using the /api/public/sign-token endpoint. See the Sign Token API documentation for complete details.
Key points:
- Tokens are tied to a specific lesson and organization
- Default expiration is 2 hours (configurable up to 24 hours)
- User attributes are embedded in the token for tracking and personalization
- Tokens cannot be modified without re-signing
SDK Options
The embedLesson() function accepts the following options:
| Option | Type | Required | Description |
|---|---|---|---|
container | HTMLElement | string | Yes | Container element or CSS selector |
token | string | Yes | JWT token from sign-token endpoint (lessonId is automatically extracted from this token) |
baseUrl | string | No | Base URL of your Teacharium instance (default: “https://www.teacharium.io ”) |
width | string | No | Iframe width (default: “100%“) |
height | string | No | Iframe height (default: “600px”) |
className | string | No | Additional CSS classes for iframe |
onLoad | function | No | Callback when lesson loads |
onComplete | function | No | Callback when lesson completes |
onProgress | function | No | Callback for progress updates |
onError | function | No | Callback for errors |
Event Handling
The SDK provides several event callbacks for tracking user progress:
onLoad
Fired when the lesson iframe has finished loading:
onLoad: () => {
console.log('Lesson is ready to play');
// Hide loading indicator, etc.
}onComplete
Fired when the user completes the entire lesson:
onComplete: (data) => {
console.log('Lesson completed!');
console.log('Lesson ID:', data.lessonId);
console.log('Completed at:', data.completedAt);
console.log('Total steps:', data.totalSteps);
// Track completion in your system
trackCompletion(data);
}onProgress
Fired when the user progresses through the lesson:
onProgress: (data) => {
const percent = (data.currentStep / data.totalSteps) * 100;
console.log(`Progress: ${percent.toFixed(1)}%`);
// Update progress bar
updateProgressBar(percent);
}onError
Fired when an error occurs:
onError: (error) => {
console.error('Lesson error:', error.message);
// Show error message to user
showErrorNotification(error.message);
}Advanced Examples
React Integration
import { useEffect, useRef } from 'react';
import { embedLesson } from '@teacharium/embed-sdk';
function LessonEmbed({ token, onComplete }) {
const containerRef = useRef(null);
const embedRef = useRef(null);
useEffect(() => {
if (containerRef.current && token) {
embedRef.current = embedLesson({
container: containerRef.current,
token: token,
// baseUrl and lessonId are handled automatically
onComplete: (data) => {
console.log('Lesson completed!', data);
onComplete?.(data);
},
onProgress: (data) => {
console.log(`Progress: ${data.currentStep}/${data.totalSteps}`);
}
});
}
// Cleanup on unmount
return () => {
if (embedRef.current) {
embedRef.current.destroy();
}
};
}, [token, onComplete]);
return (
<div
ref={containerRef}
style={{ width: '100%', height: '600px' }}
/>
);
}
export default LessonEmbed;Vue.js Integration
<template>
<div ref="lessonContainer" class="lesson-embed"></div>
</template>
<script>
import { embedLesson } from '@teacharium/embed-sdk';
export default {
name: 'LessonEmbed',
props: {
token: {
type: String,
required: true
}
},
data() {
return {
embed: null
};
},
mounted() {
this.embed = embedLesson({
container: this.$refs.lessonContainer,
token: this.token,
// baseUrl and lessonId are handled automatically
onComplete: (data) => {
this.$emit('complete', data);
}
});
},
beforeUnmount() {
if (this.embed) {
this.embed.destroy();
}
}
};
</script>
<style scoped>
.lesson-embed {
width: 100%;
height: 600px;
}
</style>Responsive Embedding
Create a responsive embed that adjusts to different screen sizes:
embedLesson({
container: '#lesson',
token: token,
width: '100%',
height: '80vh', // 80% of viewport height
className: 'responsive-lesson-iframe'
});.responsive-lesson-iframe {
max-width: 1200px;
margin: 0 auto;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
@media (max-width: 768px) {
.responsive-lesson-iframe {
height: 100vh !important;
border-radius: 0;
}
}Progress Tracking
Implement a visual progress bar that updates as the user progresses:
<div class="progress-container">
<div class="progress-bar" id="progress-bar"></div>
<div class="progress-text" id="progress-text">0%</div>
</div>
<div id="lesson-container"></div>embedLesson({
container: '#lesson-container',
token: token,
onProgress: (data) => {
const percent = (data.currentStep / data.totalSteps) * 100;
document.getElementById('progress-bar').style.width = `${percent}%`;
document.getElementById('progress-text').textContent = `${Math.round(percent)}%`;
},
onComplete: (data) => {
// Redirect to success page or show completion message
window.location.href = '/lesson-complete';
}
});.progress-container {
width: 100%;
height: 30px;
background: #e0e0e0;
border-radius: 4px;
position: relative;
margin-bottom: 20px;
}
.progress-bar {
height: 100%;
background: linear-gradient(90deg, #4caf50, #8bc34a);
border-radius: 4px;
transition: width 0.3s ease;
width: 0%;
}
.progress-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-weight: bold;
color: #333;
}Server-Side Token Generation (Node.js/Express)
Example backend endpoint for generating tokens:
const express = require('express');
const app = express();
app.post('/api/embed-token', async (req, res) => {
const { lessonId, learnerId, userId, sessionId } = req.body;
try {
const response = await fetch('https://www.teacharium.io/api/public/sign-token', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.TEACHARIUM_PUBLIC_KEY}.${process.env.TEACHARIUM_SECRET_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
lessonId: lessonId,
learnerId: learnerId, // Required: anonymous identifier for the learner
userAttributes: {
userId: userId,
sessionId: sessionId,
timestamp: new Date().toISOString()
},
timeout: 7200
})
});
if (!response.ok) {
throw new Error('Failed to generate token');
}
const { token, expiresAt } = await response.json();
res.json({ token, expiresAt });
} catch (error) {
console.error('Error generating token:', error);
res.status(500).json({ error: 'Failed to generate embed token' });
}
});Important: Do not pass PII such as email addresses in userAttributes. Use anonymous identifiers like user IDs or session IDs instead.
Security Best Practices
-
Never expose API keys in client-side code
- Always generate tokens on your backend server
- Store API credentials in environment variables
-
Do not include PII in tokens
- Never include personally identifiable information such as email addresses, full names, phone numbers, or addresses in
userAttributes - Use anonymous identifiers like user IDs, session IDs, or account numbers
- This protects user privacy and helps comply with data protection regulations (GDPR, CCPA, etc.)
- Embedded tokens may be visible in browser URLs or logs
- Never include personally identifiable information such as email addresses, full names, phone numbers, or addresses in
-
Use appropriate token expiration times
- Set
timeoutbased on expected lesson duration - Default 2 hours is suitable for most lessons
- Maximum 24 hours for longer courses
- Set
-
Include user identification in tokens
- Use
userAttributesto track which user is taking the lesson - Include anonymous identifiers like user IDs or session IDs
- This data is securely embedded in the token
- Use
-
Validate token ownership
- The API automatically validates that tokens match the requested lesson
- Tokens cannot be reused for different lessons
-
Use HTTPS
- Always serve your embedding page over HTTPS
- Teacharium requires HTTPS for API requests
Troubleshooting
Token Invalid or Expired
Error: “Token verification failed: Token expired”
Solution: Generate a new token. Tokens expire after the specified timeout period.
Lesson Not Found
Error: “Lesson not found or access denied”
Causes:
- The lesson ID is incorrect
- The lesson hasn’t been published
- The lesson belongs to a different organization
Solution: Verify the lesson ID and ensure the lesson is published.
CORS Errors
Error: “Cross-origin request blocked”
Solution: The embed player page includes proper CORS headers. Ensure you’re using the correct base URL and the lesson ID matches the token.
Iframe Not Loading
Causes:
- Content Security Policy (CSP) restrictions on your page
- X-Frame-Options preventing embedding
Solution: Ensure your page allows iframe embedding:
<meta http-equiv="Content-Security-Policy" content="frame-src 'self' https://your-teacharium-domain.com">API Reference
For detailed API documentation, see:
- Sign Token API - Generate JWT tokens for embedding
- Public Lessons API - List available lessons
SDK Reference
For complete SDK documentation, see the @teacharium/embed-sdk READMEÂ .