Security
Protect your Pexelize integration with HMAC-based identity verification. Generate signatures on your server using your Security Key to verify that each end user is who they claim to be.
Security Key
Your Security Key is available in the Security tab of your Pexelize dashboard. It is used to generate HMAC signatures on your server to verify end-user identity.
Never expose the Security Key in client-side code. It must only be used on your server.
Clicking Regenerate in the dashboard creates a new Security Key and invalidates all existing identity signatures. Any active sessions relying on the old key will fail verification. Plan a coordinated rollover if you regenerate in production.
How it works
- When a user loads the editor, your server generates an HMAC-SHA256 signature using the Security Key and the user's unique identifier.
- The signature is passed to the Pexelize editor alongside the user ID.
- Pexelize verifies the signature server-side before granting access, ensuring the request is authentic and has not been tampered with.
Generating the HMAC signature
Compute an HMAC-SHA256 hash of the user's identifier using your Security Key.
- Node.js
- Python
- PHP
- Ruby
import crypto from 'crypto';
const SECURITY_KEY = process.env.PEXELIZE_SECURITY_KEY!;
function generateIdentitySignature(userId: string): string {
return crypto
.createHmac('sha256', SECURITY_KEY)
.update(userId)
.digest('hex');
}
// Express example
app.get('/api/pexelize-token', (req, res) => {
const userId = req.user.id; // from your auth middleware
const signature = generateIdentitySignature(userId);
res.json({ userId, signature });
});
import hmac
import hashlib
import os
SECURITY_KEY = os.environ["PEXELIZE_SECURITY_KEY"]
def generate_identity_signature(user_id: str) -> str:
return hmac.new(
SECURITY_KEY.encode(),
user_id.encode(),
hashlib.sha256,
).hexdigest()
# Flask example
@app.route("/api/pexelize-token")
def pexelize_token():
user_id = current_user.id # from your auth system
signature = generate_identity_signature(user_id)
return {"userId": user_id, "signature": signature}
$securityKey = getenv('PEXELIZE_SECURITY_KEY');
function generateIdentitySignature(string $userId): string {
global $securityKey;
return hash_hmac('sha256', $userId, $securityKey);
}
// Laravel example
Route::get('/api/pexelize-token', function (Request $request) {
$userId = $request->user()->id;
$signature = generateIdentitySignature($userId);
return response()->json([
'userId' => $userId,
'signature' => $signature,
]);
});
require 'openssl'
SECURITY_KEY = ENV['PEXELIZE_SECURITY_KEY']
def generate_identity_signature(user_id)
OpenSSL::HMAC.hexdigest('sha256', SECURITY_KEY, user_id.to_s)
end
# Rails example
class PexelizeController < ApplicationController
def token
user_id = current_user.id.to_s
signature = generate_identity_signature(user_id)
render json: { userId: user_id, signature: signature }
end
end
Passing the signature to the editor
Fetch the signature from your server and provide it via the identity option.
- React
- Vue
- Angular
- Vanilla JS
import { useEffect, useState, useRef } from 'react';
import { PexelizeEditor, PexelizeEditorRef } from '@pexelize/react-editor';
function SecureEditor() {
const editorRef = useRef<PexelizeEditorRef>(null);
const [identity, setIdentity] = useState<{
userId: string;
signature: string;
} | null>(null);
useEffect(() => {
fetch('/api/pexelize-token')
.then((res) => res.json())
.then(setIdentity);
}, []);
if (!identity) return <div>Loading...</div>;
return (
<PexelizeEditor
ref={editorRef}
editorKey="pk_your_key"
editorMode="email"
identity={{
userId: identity.userId,
signature: identity.signature,
}}
/>
);
}
<template>
<div v-if="!identity">Loading...</div>
<PexelizeEditor
v-else
ref="editorRef"
editor-key="pk_your_key"
editor-mode="email"
:identity="identity"
/>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { PexelizeEditor } from '@pexelize/vue-editor';
const editorRef = ref<InstanceType<typeof PexelizeEditor>>();
const identity = ref<{ userId: string; signature: string } | null>(null);
onMounted(async () => {
const res = await fetch('/api/pexelize-token');
identity.value = await res.json();
});
</script>
import { Component, ViewChild, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { PexelizeEditorComponent } from '@pexelize/angular-editor';
@Component({
selector: 'app-secure-editor',
standalone: true,
imports: [PexelizeEditorComponent],
template: `
@if (identity) {
<pexelize-editor
#editor
editorKey="pk_your_key"
editorMode="email"
[identity]="identity"
></pexelize-editor>
} @else {
<div>Loading...</div>
}
`,
})
export class SecureEditorComponent implements OnInit {
@ViewChild('editor') editorComp!: PexelizeEditorComponent;
identity: { userId: string; signature: string } | null = null;
constructor(private http: HttpClient) {}
ngOnInit() {
this.http
.get<{ userId: string; signature: string }>('/api/pexelize-token')
.subscribe((data) => (this.identity = data));
}
}
async function initSecureEditor() {
const res = await fetch('/api/pexelize-token');
const { userId, signature } = await res.json();
pexelize.init({
containerId: 'editor-container',
editorKey: 'pk_your_key',
editorMode: 'email',
identity: {
userId,
signature,
},
});
}
initSecureEditor();
Key rotation
When you click Regenerate in the dashboard:
- A new Security Key is issued immediately.
- All existing signatures become invalid.
- Users with active sessions will need to re-authenticate.
To minimize disruption during rotation:
- Deploy your server update with the new key.
- Click Regenerate in the dashboard.
- Active users will automatically re-verify on their next interaction.
If your application cannot tolerate any verification failures, support both old and new keys briefly by accepting signatures from either key during the transition window, then remove the old key.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
IDENTITY_VERIFICATION_FAILED | Signature mismatch | Ensure the user ID passed to the editor matches exactly what was signed on the server |
IDENTITY_SIGNATURE_EXPIRED | Key was regenerated | Update your server with the new Security Key from the dashboard |
IDENTITY_MISSING | No identity provided | Pass the identity option with both userId and signature |
Next steps
- Collaboration -- add role-based permissions alongside identity verification
- Image Upload -- secure upload endpoints with the same user identity
- Iframe & CSP -- configure Content Security Policy headers