SSO Comment System
This guide explains how to integrate your website’s authentication system with the Arena Comments widget using a new secure SSO exchange route. This integration allows your authenticated users to automatically sign into Arena when they interact with the embedded widget on your site.
Step 1: Generate an RSA Key Pair
You’ll use your private key to sign JWTs and share the public key with Arena so we can verify them.
🔐 Key Generation (Bash)
# Generate 2048-bit RSA private key
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
# Extract the public key
openssl rsa -pubout -in private_key.pem -out public_key.pemIMPORTANT: Keep private_key.pem secret and secure. Load it from an environment variable. Share only public_key.pem and your JWT endpoint URL with Arena.
Step 2: Implement the Arena JWT SSO Endpoint
Your backend must expose a secure route (e.g. /arena/sso-jwt) that:
- Verifies the user is authenticated on your site
- Generates a short-lived JWT containing user identity
- Signs it with your RSA private key
- Returns the JWT in the response
🧾 JWT Payload Claims
{
"sub": "[email protected]", // Same as email
"email": "[email protected]",
"displayName": "Jane Doe",
"photoURL": "https://example.com/avatar.jpg",
"iat": 1720452000, // Issued at (Unix timestamp)
"exp": 1720452300, // Expires (1 minute from now)
"iss": "your_website_sso_id" // Your website SSO issuer ID, provided by Arena's tech team during onboarding
}
IMPORTANT: Ensure the route is forgery-proof
The custom route /arena/sso-jwt must strictly issue signed JWTs only for the currently authenticated end user, because allowing arbitrary user IDs or names opens a huge security risk: someone could impersonate other users by forging their identity in the payload.
Bad example: The client POSTs { "id": "1234", "name": "Alice" } to /arena/sso-jwt, and the server signs that — this lets a malicious user pretend to be anyone.
OK example: The client sends only its session cookie, the server validates that session, looks up the user in the database to get their real id and name, and then signs a JWT for that user.
🔧 Node.js Example (using jsonwebtoken)
const jwt = require('jsonwebtoken');
const express = require('express');
const app = express();
// must contain the contents of private_key.pem generated in step 1
const PRIVATE_KEY = process.env.ARENA_SSO_PRIVATE_KEY;
// provided by Arena's tech team during onboarding
const ARENA_SSO_ISSUER_ID = process.env.ARENA_SSO_ISSUER_ID;
app.get('/arena/sso-jwt', (req, res) => {
// replace whis validation with your website's own auth validation
const user = req.authenticatedUser;
if (!user) return res.status(401).json({ error: 'Unauthorized' });
const payload = {
sub: user.id, // must be a string
email: user.email,
displayName: user.name,
photoURL: user.avatar,
// handle is a nick name unique to your website
// handle must pass the following regex: ^[a-z0-9_]{1,32}$
handle: user.handle,
joinedAt: user.joinedAt, // in unix-milli (e.g., 1761256322000)
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 60, // 1-minute expiry
iss: ARENA_SSO_ISSUER_ID
};
const token = jwt.sign(payload, PRIVATE_KEY, { algorithm: 'RS256' });
res.json({ token });
});🔧 PHP Example (using firebase/php-jwt)
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
// must contain the contents of private_key.pem generated in step 1
$privateKey = getenv('ARENA_SSO_PRIVATE_KEY');
// provided by Arena's tech team during onboarding
$ssoIssuerID = getenv('ARENA_SSO_ISSUER_ID');
// replace whis validation with your website's own auth validation
$user = get_authenticated_user();
if (!$user) {
http_response_code(401);
echo json_encode(['error' => 'Unauthorized']);
exit;
}
$payload = [
'sub' => $user['id'], // must be a string
'email' => $user['email'],
'displayName' => $user['name'],
'photoURL' => $user['avatar'],
// handle is a nick name unique to your website
// handle must pass the following regex: ^[a-z0-9_]{1,32}$
'handle' => $user['handle'],
'joinedAt' => $user['joinedAt'], // in unix-milli (e.g., 1761256322000)
'iat' => time(),
'exp' => time() + 60,
'iss' => $ssoIssuerID
];
$jwt = JWT::encode($payload, $privateKey, 'RS256');
echo json_encode(['token' => $jwt]);Step 3: Send Information to Arena
Send the following to the Arena team:
- Your public key (contents of
public_key.pem)
Arena will register your public key and endpoint to verify JWTs issued by your system.
Step 4: Setup Arena snippets into your website HTML
-
Follow instructions here to install Arena Comment System in your website
-
Add the SSO Flag:
To enable the SSO login override, include the data-sso-enabled="true" attribute in the Comment System's div element. This flag informs the system to bypass the default authentication modal and trigger a custom event instead.
Example:
<div class="arena-comments-widget" data-site-slug="[YOUR-SITE-SLUG]" data-sso-enabled="true" />- Insert the following code below the Comment System
<div>:
<script>
class ArenaSSO {
constructor() {
// Configuration - replace with your values
this.ssoBackendUrl = "https://your-backend.com/arena/sso-jwt";
this.isArenaLoaded = false;
}
async init() {
// Wait for Arena widget to load
document.addEventListener("arena-loaded", () => {
this.isArenaLoaded = true;
});
}
async getJWTToken() {
// Request JWT token from your backend
const response = await fetch(this.ssoBackendUrl, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const data = await response.json();
return data.token;
}
async authenticateUser() {
// Check if Arena is loaded before proceeding
if (!this.isArenaLoaded) return;
// Get JWT token from backend
const jwtToken = await this.getJWTToken();
// Call Arena's SSO v2 Exchange method
const result = await window.arena.auth.ssoV2Exchange(jwtToken);
}
// Public method to authenticate a specific user
async login() {
await this.authenticateUser();
}
// Public method to logout
logout() {
if (window.arena && window.arena.auth && window.arena.auth.logout) {
window.arena.auth.logout();
}
}
}
// Initialize SSO when page loads
document.addEventListener("DOMContentLoaded", () => {
window.arenaSSO = new ArenaSSO();
});
</script>- In your application use the exposed methods to login or logout:
// Login a specific user
window.arenaSSO.login();
// Logout current user
window.arenaSSO.logout();- Listen to widget login/logout events
The Arena Comments widget triggers two events that notify your application when the user interacts with the widget’s authentication buttons:
arena-comments-custom-login: The user clicks Login inside the widget
arena-comments-custom-logout: The user clicks Logout inside the widget
Example:
document.addEventListener('arena-comments-custom-login', () => {
// This runs when the user clicks the login button inside the widget
// Add your custom login logic
window.arenaSSO.login();
});
document.addEventListener('arena-comments-custom-logout', () => {
// This runs when the user clicks the logout button inside the widget
// Add your custom logout logic
window.arenaSSO.logout();
});
Note:
The arena-comments-custom-login event is also triggered when a non-authenticated user interacts with any action that requires authentication — such as reacting, replying, or sending a comment.
- (Optional) Register custom account action
If you want to handle account actions yourself, you can override the default widget behavior by adding the handleAccountClick to window.arena.auth.
When this function is present, the default account modal will not be displayed.
Option A — Override the account button behavior (custom handler):
// Ensure arena.auth exists
window.arena = window.arena || {};
window.arena.auth = window.arena.auth || {};
// Custom handler for account clicks
window.arena.auth.handleAccountClick = function () {
// Your custom logic goes here.
// This will completely replace the default modal behavior.
console.log('Account button clicked — running custom handler');
// Example: redirect users to your own account page
window.location.href = '/my-account';
};
With this handler in place, clicking the account button in the widget will trigger your custom code instead of opening the default Arena modal.
Option B — Hide the account button entirely
If you prefer to handle authentication fully outside the widget, you can hide the account button with data-hide-account-button="true":
<div
class="arena-comments-widget"
data-site-slug="[YOUR-SITE-SLUG]"
data-sso-enabled="true"
data-hide-account-button="true"/>When this attribute is set to true, the widget will not render any login/logout button.
Updated 17 days ago