Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.fanfare.io/llms.txt

Use this file to discover all available pages before exploring further.

Authentication Best Practices

This guide covers secure authentication practices for integrating with Fanfare.

API Key Management

Types of Keys

Key TypeFormatUsageExposure
Publishable Keypk_test_... / pk_live_...Client-side SDKSafe for clients
Secret Keysk_test_... / sk_live_...Server-side APINever expose

Publishable Keys

Publishable keys are designed for client-side use:
// Client-side: OK
const client = new FanfareClient({
  publishableKey: process.env.NEXT_PUBLIC_FANFARE_KEY,
});
Capabilities:
  • Initialize SDK
  • Enter experiences
  • Check queue status
  • Consumer authentication
Limitations:
  • Cannot access admin functions
  • Cannot read other users’ data
  • Rate limited more aggressively

Secret Keys

Secret keys provide full API access:
// Server-side ONLY
const response = await fetch("https://admin.fanfare.io/api/experiences", {
  headers: {
    Authorization: `Bearer ${process.env.FANFARE_SECRET_KEY}`,
  },
});
Never expose secret keys in client-side code, mobile apps, or public repositories.

Secure Key Storage

Environment Variables:
# .env (never commit this file)
FANFARE_SECRET_KEY=sk_live_...
FANFARE_PUBLISHABLE_KEY=pk_live_...
Secrets Managers:
  • AWS Secrets Manager
  • Google Secret Manager
  • HashiCorp Vault
  • Vercel Environment Variables

Key Rotation

Rotate keys periodically or after potential exposure:
  1. Generate new key in dashboard
  2. Update your application
  3. Test thoroughly
  4. Revoke old key
// Handle key rotation gracefully
const client = new FanfareClient({
  publishableKey: process.env.FANFARE_KEY,
  onUnauthorized: () => {
    // Key may have been rotated
    console.error("API key invalid - check for rotation");
  },
});

Consumer Authentication

Authentication Methods

Fanfare supports multiple consumer authentication methods:
MethodUse CaseSecurity Level
GuestAnonymous accessBasic
Email OTPVerified emailMedium
Phone OTPVerified phoneMedium
SSOEnterpriseHigh

Guest Authentication

For anonymous access with device binding:
// Create guest session
const session = await client.auth.guest();

// Guest has limited capabilities
// - Enter experiences
// - Join queues
// - Cannot link to authenticated account later

OTP Authentication

For verified consumer identity:
// Step 1: Request OTP
await client.auth.requestOTP({
  method: "email",
  email: "[email protected]",
});

// Step 2: Verify OTP
const session = await client.auth.verifyOTP({
  email: "[email protected]",
  code: "123456",
});
OTP Security:
  • Codes expire after 10 minutes
  • Limited retry attempts
  • Rate limited requests
  • Invalid codes logged for monitoring

Session Management

// Get current session
const session = client.auth.getSession();

// Check authentication state
if (session?.isAuthenticated) {
  // User is verified
} else if (session?.isGuest) {
  // Guest session
} else {
  // No session
}

// Refresh session
await client.auth.refresh();

// Logout
await client.auth.logout();

Token Security

JWT Structure

Consumer tokens are JWTs with these claims:
{
  "sub": "consumer_id",
  "org": "organization_id",
  "iat": 1705312800,
  "exp": 1705399200,
  "type": "consumer"
}

Token Handling

localStorage is vulnerable to XSS attacks. For high-security applications, consider httpOnly cookies.
URLs can be logged, cached, and shared. Use headers for token transmission.
client.on("auth:session-expired", async () => {
  // Attempt refresh
  try {
    await client.auth.refresh();
  } catch {
    // Redirect to login
    redirectToLogin();
  }
});
When processing handoff tokens, always validate on your server.
// Server-side validation
const isValid = await validateHandoffToken(token, secretKey);

Server-Side Authentication

Validating Consumer Requests

When consumers interact with your backend:
// Your API endpoint
app.post("/api/checkout", async (req, res) => {
  const handoffToken = req.body.handoffToken;

  // Validate with Fanfare
  const response = await fetch("https://admin.fanfare.io/api/handoffs/validate", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.FANFARE_SECRET_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ token: handoffToken }),
  });

  if (!response.ok) {
    return res.status(401).json({ error: "Invalid handoff" });
  }

  const handoff = await response.json();
  // Process checkout with validated consumer
});

Webhook Authentication

Verify webhook signatures:
import crypto from "crypto";

function verifyWebhookSignature(payload: string, signature: string, secret: string): boolean {
  const expected = crypto.createHmac("sha256", secret).update(payload).digest("hex");

  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

app.post("/webhooks/fanfare", (req, res) => {
  const signature = req.headers["x-fanfare-signature"];

  if (!verifyWebhookSignature(req.rawBody, signature, WEBHOOK_SECRET)) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  // Process webhook
});

Security Checklist

Development

  • Use test keys only in development
  • Never commit keys to version control
  • Add .env to .gitignore
  • Use separate test and production keys

Production

  • Use live keys in production
  • Store keys in secrets manager
  • Enable key rotation policy
  • Monitor for unauthorized access
  • Set up alerts for authentication failures

Code Review

  • No hardcoded credentials
  • Secret keys only in server-side code
  • Token handling follows best practices
  • Webhook signatures verified
  • Error messages don’t leak sensitive info

Common Mistakes

Exposing secret keys

Wrong: Including sk_ keys in client bundles Right: Only use pk_ keys client-side

Not validating handoffs

Wrong: Trusting client-sent handoff tokens Right: Always validate tokens server-side

Ignoring token expiration

Wrong: Assuming tokens are always valid Right: Handle expiration and refresh

Skipping webhook verification

Wrong: Processing webhooks without checking signature Right: Always verify webhook signatures