Skip to main content

Guest Accounts

OpenSecret provides support for guest accounts, allowing users to access your application without providing an email address. This guide explains how to implement and manage guest accounts in your application.

Overview

Guest accounts offer several benefits:

  • Reduce friction in user onboarding
  • Allow users to try your application before committing
  • Support scenarios where email collection isn't initially required
  • Maintain security and user-specific data without email authentication

Prerequisites

Before implementing guest accounts, make sure:

  1. Your application is wrapped with OpenSecretProvider
  2. Guest accounts are enabled in your project settings

Creating Guest Accounts

To create a guest account, use the signUpGuest method:

import { useState } from "react";
import { useOpenSecret } from "@opensecret/react";

function GuestSignupForm() {
const os = useOpenSecret();
const [password, setPassword] = useState("");
const [inviteCode, setInviteCode] = useState("");
const [guestId, setGuestId] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");

async function handleGuestSignup(e) {
e.preventDefault();
setLoading(true);
setError("");

try {
const response = await os.signUpGuest(password, inviteCode);
setGuestId(response.id);
// Guest is now registered and logged in
} catch (err) {
setError(err instanceof Error ? err.message : "Guest signup failed");
} finally {
setLoading(false);
}
}

return (
<div>
<h3>Create Guest Account</h3>
<form onSubmit={handleGuestSignup}>
<div className="form-group">
<label htmlFor="password">Password:</label>
<input
id="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
minLength={8}
required
disabled={loading}
/>
</div>
<div className="form-group">
<label htmlFor="inviteCode">Invite Code:</label>
<input
id="inviteCode"
type="text"
value={inviteCode}
onChange={(e) => setInviteCode(e.target.value)}
required
disabled={loading}
/>
</div>
{error && <div className="error">{error}</div>}
<button type="submit" disabled={loading}>
{loading ? "Creating..." : "Create Guest Account"}
</button>
</form>

{guestId && (
<div className="success-message">
<h4>Guest Account Created!</h4>
<p><strong>Important:</strong> Please save your guest ID:</p>
<div className="guest-id">{guestId}</div>
<p>You will need this ID to log in again. Without it, you cannot access your account.</p>
<button onClick={() => navigator.clipboard.writeText(guestId)}>
Copy ID to Clipboard
</button>
</div>
)}
</div>
);
}

Guest Login

Once a guest account is created, users can log in using their guest ID and password:

import { useState } from "react";
import { useOpenSecret } from "@opensecret/react";

function GuestLoginForm() {
const os = useOpenSecret();
const [guestId, setGuestId] = useState("");
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");

async function handleGuestLogin(e) {
e.preventDefault();
setLoading(true);
setError("");

try {
await os.signInGuest(guestId, password);
// Guest is now logged in
} catch (err) {
setError(err instanceof Error ? err.message : "Guest login failed");
} finally {
setLoading(false);
}
}

return (
<div>
<h3>Guest Login</h3>
<form onSubmit={handleGuestLogin}>
<div className="form-group">
<label htmlFor="guestId">Guest ID:</label>
<input
id="guestId"
type="text"
value={guestId}
onChange={(e) => setGuestId(e.target.value)}
required
disabled={loading}
/>
</div>
<div className="form-group">
<label htmlFor="password">Password:</label>
<input
id="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
disabled={loading}
/>
</div>
{error && <div className="error">{error}</div>}
<button type="submit" disabled={loading}>
{loading ? "Logging in..." : "Login as Guest"}
</button>
</form>
</div>
);
}

Authentication UI with Guest Support

Here's a complete authentication UI example that supports both regular and guest accounts:

import { useState } from "react";
import { useOpenSecret } from "@opensecret/react";

function AuthenticationUI() {
const os = useOpenSecret();
const [activeTab, setActiveTab] = useState("login");

// Log out current user
function handleLogout() {
os.signOut();
}

// Render authenticated user info
if (os.auth.user) {
const isGuest = !os.auth.user.email;

return (
<div className="auth-container">
<h3>Welcome, {os.auth.user.name || "Guest User"}</h3>

<div className="user-info">
<div className="info-row">
<span className="label">ID:</span>
<span className="value">{os.auth.user.id}</span>
</div>
{os.auth.user.email && (
<div className="info-row">
<span className="label">Email:</span>
<span className="value">{os.auth.user.email}</span>
</div>
)}
<div className="info-row">
<span className="label">Account Type:</span>
<span className="value">{isGuest ? "Guest Account" : "Regular Account"}</span>
</div>
</div>

<button onClick={handleLogout} className="logout-button">
Log Out
</button>

</div>
);
}

// Render login/signup UI for unauthenticated users
return (
<div className="auth-container">
<div className="auth-tabs">
<button
className={activeTab === "login" ? "active" : ""}
onClick={() => setActiveTab("login")}
>
Login
</button>
<button
className={activeTab === "signup" ? "active" : ""}
onClick={() => setActiveTab("signup")}
>
Sign Up
</button>
<button
className={activeTab === "guest" ? "active" : ""}
onClick={() => setActiveTab("guest")}
>
Guest Access
</button>
</div>

<div className="auth-form-container">
{activeTab === "login" && <RegularLoginForm />}
{activeTab === "signup" && <RegularSignupForm />}
{activeTab === "guest" && (
<div className="guest-options">
<div className="guest-option-tabs">
<button
className={activeGuestTab === "create" ? "active" : ""}
onClick={() => setActiveGuestTab("create")}
>
Create Guest Account
</button>
<button
className={activeGuestTab === "login" ? "active" : ""}
onClick={() => setActiveGuestTab("login")}
>
Guest Login
</button>
</div>

{activeGuestTab === "create" && <GuestSignupForm />}
{activeGuestTab === "login" && <GuestLoginForm />}
</div>
)}
</div>
</div>
);
}

// Components for RegularLoginForm and RegularSignupForm would be implemented similarly
// to the examples in the Authentication guide

Handling Guest Account Data

Guest accounts can use all the same features as regular accounts:

Key-Value Storage

// Store data in a guest account
await os.put("preferences", JSON.stringify({ theme: "dark" }));

// Retrieve data
const preferences = await os.get("preferences");

Cryptographic Operations

// Get public key for a guest account
const { public_key } = await os.getPublicKey("schnorr");

// Sign messages
const messageBytes = new TextEncoder().encode("Hello, world!");
const signature = await os.signMessage(messageBytes, "schnorr");

Guest accounts are long-lived accounts identified by UUID and password.

Security Considerations

  1. Password security is critical: Since recovery via email isn't possible for guest accounts, encourage strong passwords

  2. Explain the importance of saving the guest ID: Make it clear that losing the guest ID means losing access to the account

  3. Consider session persistence: For guest accounts, you might want longer-lived tokens to reduce the frequency of logins

  4. Rate limit guest account creation: To prevent abuse, consider rate limiting guest account creation by IP address

  5. Treat guest accounts as permanent anonymous accounts: Guest accounts do not have email recovery, so users must keep both their guest ID and password.

Best Practices

  1. Simplify the guest experience: Minimize the information required to create a guest account

  2. Provide clear guest ID instructions: Make it obvious that users need to save their guest ID

  3. Offer registered accounts separately: If users need email recovery, guide them to create a registered account before relying on guest-only data

  4. Implement guest ID recovery aids: Consider client-side storage or reminders that help users retain their guest ID

  5. Be transparent about limitations: Clearly communicate any feature limitations for guest accounts

Example: Anonymous Authentication Flow

A common pattern is to support guest access without treating it as a temporary account:

  1. Start as guest: User starts with a guest account for immediate access
  2. Save guest credentials: User stores the guest ID and password somewhere durable
  3. Store user data: Application stores user preferences and progress under the guest account
  4. Create registered accounts separately: If the user wants email recovery, direct them to create a registered account before storing important data there

This provides a frictionless anonymous experience while keeping the recovery limitations clear.

Next Steps