> ## 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.

# Anonymous consumers

# Anonymous Consumers Guide

Learn how to implement guest checkout flows with anonymous consumer authentication.

## Overview

Anonymous (guest) authentication allows consumers to participate in experiences without providing personal information. This is ideal for guest checkout flows, quick access scenarios, or when you want to minimize friction.

**What you'll learn:**

* Creating guest sessions
* Managing anonymous consumer lifecycles
* Persisting anonymous sessions across visits
* Converting anonymous consumers to identified

**Complexity:** Beginner
**Time to complete:** 20 minutes

## Prerequisites

* Fanfare SDK installed and configured
* Basic understanding of authentication concepts

## How Anonymous Authentication Works

<img src="https://mintcdn.com/fanfare/9lBxxAA0GJkGRgw-/images/guides/anonymous-consumer-flow.webp?fit=max&auto=format&n=9lBxxAA0GJkGRgw-&q=85&s=cfe021ddf746bc0d26073b27a4139153" alt="Anonymous consumer flow diagram showing session check, restore or create guest, and participate." width="1774" height="887" data-path="images/guides/anonymous-consumer-flow.webp" />

## Step 1: Create a Guest Session

The SDK provides a simple method to create an anonymous session:

```typescript theme={null}
import { useFanfareAuth } from "@fanfare-io/fanfare-sdk-react";

function AuthButton() {
  const { isAuthenticated, isGuest, guest, session } = useFanfareAuth();

  const handleGuestAuth = async () => {
    try {
      const guestSession = await guest();
      console.log("Guest session created:", guestSession.consumerId);
    } catch (error) {
      console.error("Failed to create guest session:", error);
    }
  };

  if (isAuthenticated) {
    return (
      <div>
        <p>Authenticated as: {isGuest ? "Guest" : "Identified"}</p>
        <p>Consumer ID: {session?.consumerId}</p>
      </div>
    );
  }

  return <button onClick={handleGuestAuth}>Continue as Guest</button>;
}
```

### Using the Core SDK

```typescript theme={null}
import initFanfare from "@fanfare-io/fanfare-sdk-core";

const sdk = await initFanfare({
  organizationId: "org_xxx",
  publishableKey: "pk_live_xxx",
});

// Check if already authenticated
const status = sdk.auth.check();

if (!status.isAuthenticated) {
  // Create guest session
  const session = await sdk.auth.guest();
  console.log("Guest ID:", session.guestId);
  console.log("Consumer ID:", session.consumerId);
}
```

## Step 2: Automatic Guest Authentication

For a frictionless experience, automatically create a guest session when needed:

```tsx theme={null}
import { useFanfareAuth } from "@fanfare-io/fanfare-sdk-react";
import { useEffect, useState } from "react";

function useAutoGuestAuth() {
  const { isAuthenticated, guest } = useFanfareAuth();
  const [isReady, setIsReady] = useState(false);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    async function authenticate() {
      if (isAuthenticated) {
        setIsReady(true);
        return;
      }

      try {
        await guest();
        setIsReady(true);
      } catch (err) {
        setError(err instanceof Error ? err : new Error("Auth failed"));
      }
    }

    authenticate();
  }, [isAuthenticated, guest]);

  return { isReady, error };
}

// Usage
function ExperienceWrapper({ children }: { children: React.ReactNode }) {
  const { isReady, error } = useAutoGuestAuth();

  if (error) {
    return <p>Failed to initialize: {error.message}</p>;
  }

  if (!isReady) {
    return <p>Initializing...</p>;
  }

  return <>{children}</>;
}
```

## Step 3: Understanding the Guest Session

The guest session includes:

```typescript theme={null}
interface GuestSession {
  type: "guest";
  consumerId: string; // Unique consumer identifier (UUIDv7)
  guestId: string; // Same as consumerId for guests
  expiresAt: string; // ISO 8601 timestamp
  sessionSignals?: Record<string, unknown>;
}
```

### Session Lifecycle

1. **Creation**: Guest session created via `/auth/guest` endpoint
2. **Persistence**: Session stored in localStorage (default behavior)
3. **Token Refresh**: Access token refreshes automatically before expiry
4. **Expiration**: Session expires based on `sessionDuration` config (default 1 hour)

## Step 4: Session Persistence

Sessions persist automatically via localStorage:

```typescript theme={null}
// The SDK handles this automatically, but you can control it:
<FanfareProvider
  organizationId="org_xxx"
  publishableKey="pk_live_xxx"
  autoRestore={true}  // Restore session on mount (default)
  autoResume={true}   // Resume active operations (default)
/>
```

### Manual Session Management

```typescript theme={null}
// Restore session manually
const { session, experiences } = await sdk.restore();

if (session) {
  console.log("Session restored:", session.consumerId);

  // Resume polling for active queues
  await sdk.resume();

  // Check active participations
  console.log("Active queues:", Object.keys(experiences.queues));
  console.log("Active draws:", Object.keys(experiences.draws));
}
```

### Clearing Session

```typescript theme={null}
// Logout clears the session
await sdk.auth.logout();

// This clears:
// - Access token
// - Refresh token
// - Session from localStorage
// - Active participations (locally)
```

## Step 5: Cross-Tab Synchronization

The SDK synchronizes sessions across browser tabs automatically:

```typescript theme={null}
<FanfareProvider
  organizationId="org_xxx"
  publishableKey="pk_live_xxx"
  sync={{
    enabled: true,           // Enable cross-tab sync
    syncKeys: [              // What to synchronize
      "session",
      "activeQueues",
      "activeDraws",
      "refreshToken"
    ],
    channelName: "fanfare-sdk-sync"  // BroadcastChannel name
  }}
/>
```

### What Gets Synchronized

* **Session state**: Login/logout syncs across tabs
* **Active participations**: Queue/draw entries shared
* **Token refresh**: One tab refreshes, all tabs update

## Step 6: Session Integrity

Guest sessions include SDK-managed integrity signals for security:

```typescript theme={null}
<FanfareProvider
  organizationId="org_xxx"
  publishableKey="pk_live_xxx"
/>
```

### Integrity Benefits

* **Fraud prevention**: Detect suspicious multi-account behavior
* **Session binding**: Keep grants scoped to the active session
* **Queue position protection**: Prevent queue position transfer

### Privacy Considerations

Work with your legal and privacy teams to disclose collection and consent requirements appropriate to your implementation.

## Step 7: Anonymous Session Events

Subscribe to authentication events:

```tsx theme={null}
import { useFanfare } from "@fanfare-io/fanfare-sdk-react";
import { useEffect } from "react";

function AuthEventHandler() {
  const fanfare = useFanfare();

  useEffect(() => {
    // New authentication
    const unsubAuth = fanfare.on("auth:authenticated", ({ session, isNew }) => {
      console.log("Authenticated:", session.consumerId);
      console.log("Is new session:", isNew);
    });

    // Session refreshed
    const unsubRefresh = fanfare.on("auth:refreshed", ({ session }) => {
      console.log("Session refreshed:", session.expiresAt);
    });

    // Logout
    const unsubLogout = fanfare.on("auth:logout", ({ reason }) => {
      console.log("Logged out:", reason);
    });

    // Auth error
    const unsubError = fanfare.on("auth:error", ({ error, context }) => {
      console.error("Auth error:", error.message, "Context:", context);
    });

    return () => {
      unsubAuth();
      unsubRefresh();
      unsubLogout();
      unsubError();
    };
  }, [fanfare]);

  return null;
}
```

## Complete Example: Guest Experience Flow

```tsx theme={null}
import { FanfareProvider, useFanfareAuth, useExperienceJourney } from "@fanfare-io/fanfare-sdk-react";
import { useEffect, useState } from "react";

function GuestExperience({ experienceId }: { experienceId: string }) {
  const { isAuthenticated, isGuest, guest, session } = useFanfareAuth();
  const { view, start } = useExperienceJourney(experienceId);
  const [isInitializing, setIsInitializing] = useState(true);

  // Step 1: Auto-authenticate as guest
  useEffect(() => {
    async function init() {
      if (!isAuthenticated) {
        await guest();
      }
      setIsInitializing(false);
    }
    init();
  }, [isAuthenticated, guest]);

  // Step 2: Start journey when authenticated
  useEffect(() => {
    if (isAuthenticated && view?.journeyStage === "ready" && !isInitializing) {
      void start();
    }
  }, [isAuthenticated, view?.journeyStage, isInitializing, start]);

  if (isInitializing) {
    return <div className="loading">Preparing your experience...</div>;
  }

  return (
    <div className="guest-experience">
      {/* Session Info */}
      <div className="session-info">
        <p>Session Type: {isGuest ? "Guest" : "Identified"}</p>
        <p>Consumer ID: {session?.consumerId?.slice(0, 8)}...</p>
      </div>

      {/* Experience Status */}
      <div className="experience-status">
        <p>Status: {view?.journeyStage ?? "loading"}</p>
      </div>

      {/* Actions */}
      <div className="actions">
        {view?.journeyStage === "routed" && view.sequence.phase === "enterable" && "enter" in view.sequence && (
          <button onClick={() => void view.sequence.enter()} className="primary-btn">
            Join Queue
          </button>
        )}

        {view?.journeyStage === "routed" && view.sequence.phase === "granted" && (
          <div className="admitted">
            <h3>You're In!</h3>
            <button onClick={() => sendAdmissionGrant(view.sequence.grant.token)} className="checkout-btn">
              Continue to Checkout
            </button>
          </div>
        )}
      </div>
    </div>
  );
}

export function App() {
  return (
    <FanfareProvider organizationId="org_xxx" publishableKey="pk_live_xxx" autoRestore={true} autoResume={true}>
      <GuestExperience experienceId="exp_product_launch" />
    </FanfareProvider>
  );
}
```

## Best Practices

### 1. Always Check Authentication First

```typescript theme={null}
const status = sdk.auth.check();

if (status.isAuthenticated) {
  // Use existing session
} else {
  // Create new guest session
  await sdk.auth.guest();
}
```

### 2. Handle Session Expiration

```typescript theme={null}
sdk.on("auth:error", async ({ error, context }) => {
  if (context === "unauthorized") {
    // Session expired, re-authenticate
    await sdk.auth.guest();
  }
});
```

### 3. Provide Clear Upgrade Path

```tsx theme={null}
function GuestBanner() {
  const { isGuest, requestOtp } = useFanfareAuth();
  const [showUpgrade, setShowUpgrade] = useState(false);

  if (!isGuest) return null;

  return (
    <div className="guest-banner">
      <p>Want to save your progress and get notifications?</p>
      <button onClick={() => setShowUpgrade(true)}>Create Account</button>

      {showUpgrade && <UpgradeForm onSubmit={(email) => requestOtp({ email })} />}
    </div>
  );
}
```

### 4. Graceful Degradation

```typescript theme={null}
async function ensureAuthenticated() {
  try {
    const status = sdk.auth.check();
    if (!status.isAuthenticated) {
      await sdk.auth.guest();
    }
    return true;
  } catch (error) {
    console.error("Auth failed:", error);
    // Show error UI or retry logic
    return false;
  }
}
```

## Troubleshooting

### Session Not Persisting

* Check localStorage is available (not in incognito)
* Verify `autoRestore` is enabled
* Check for storage quota issues

### Guest Session Fails

* Verify API credentials
* Check network connectivity
* Review console for error messages

### Cross-Tab Sync Issues

* Ensure same origin for all tabs
* Verify BroadcastChannel support
* Check sync configuration

## What's Next

* [Identified Consumers](/guides/authentication/identified-consumers) - Email/phone authentication
* [Consumer Linking](/guides/authentication/consumer-linking) - Upgrade anonymous to identified
* [SPA Integration](/guides/integration-patterns/spa-integration) - Full integration example
