Authorization

The component works with any Convex-compatible authentication provider. You can allow anonymous bookings while keeping administration private.

Who checks access?

OperationHost responsibility
Browse availabilityExpose only resources the visitor may see
Create a bookingCheck allowed dates, durations, notice periods and rate limits
Manage a bookingVerify its management token, owner or authorized administrator
Administer resources and schedulesVerify administrator permission and organization access
Reset dataKeep the operation internal

A component is isolated from your app's authentication. Calls through components.booking.* happen inside your Convex functions; they are not public browser endpoints. Each public host wrapper must enforce its own access policy.

Protect administration

First configure your auth provider so ctx.auth.getUserIdentity() returns a verified identity. A signed-in user is not automatically an administrator.

For a single-organization app, add the following builders to convex/functions.ts alongside the public builders from the quickstart. Set BOOKING_ADMIN_TOKEN_IDENTIFIERS on your Convex deployment to a JSON array of trusted administrator identity token identifiers. An empty allowlist denies access.

typescriptconvex/functions.ts
import { customCtx, customMutation, customQuery } from "convex-helpers/server/customFunctions";
import { ConvexError } from "convex/values";
import { query, mutation, type QueryCtx, type MutationCtx } from "./_generated/server";
 
async function requireAdmin(ctx: QueryCtx | MutationCtx) {
  const identity = await ctx.auth.getUserIdentity();
  const allowed: unknown = JSON.parse(process.env.BOOKING_ADMIN_TOKEN_IDENTIFIERS ?? "[]");
  if (!identity || !Array.isArray(allowed) || !allowed.includes(identity.tokenIdentifier)) {
    throw new ConvexError({ code: "FORBIDDEN", message: "Administrator access required" });
  }
  return {
    user: { userId: identity.tokenIdentifier, email: identity.email ?? "", name: identity.name },
    role: "admin" as const,
  };
}
 
export const adminQuery = customQuery(query, customCtx(requireAdmin));
export const adminMutation = customMutation(mutation, customCtx(requireAdmin));

Your admin wrappers can then call the component:

typescriptconvex/admin-example.ts
import { v } from "convex/values";
import { components } from "./_generated/api";
import { adminQuery } from "./functions";
 
export const listResources = adminQuery({
  args: { organizationId: v.string() },
  handler: async (ctx, args) =>
    ctx.runQuery(components.booking.resources.listResources, args),
});

This example grants the allowlisted administrators access to the whole app. For per-organization access, add the checks below before forwarding any operation. Complete admin wrappers are available in the demo source.

Multiple organizations

Resolve membership from the verified identity on the server. For each requested resource or booking, load it and check that its organization is one the caller may administer. Apply this to reads as well as writes, including booking details that contain names, email addresses or management tokens.

Do not trust a supplied organizationId, userId or role as proof of permission. An administrator of one organization must not be able to operate on another organization by changing an argument.

The reference public gateway uses a booking UID plus a secret management token for retrieval, cancellation and rescheduling. Treat the token like a password: return it only to the booker or an authorized administrator, and do not expose it in public booking lists. UI visibility checks alone do not protect an endpoint.

About the demo

The hosted demo intentionally allows guest administrators to edit one shared sandbox. That is a demonstration policy, not the default for your application. See running the demo for its auth and reset behavior.