agentleFS
waynesuttonwaynesutton/markdown-site/.claude/convex-setup-auth/SKILL.md

convex-setup-auth

Skill628 starsChanged 7 months agoMITView on GitHub

Set up Convex authentication with proper user management, identity mapping, and access control patterns. Use when implementing auth flows, setting up OAuth providers, or adding role-based access control.

15 other projects use a version of this, 15 of them edited.

Adopted by

Most recently taken up by

Used with

Projects with it also use clawhub-ban-appeals, clawhub-clawscan and 10 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • openclawSkill

    clawhub-ban-appeals

    Review ClawHub account-ban appeals, distinguish bans from moderation holds or blocked releases, and prepare an evidence-backed moderation decision. Route validated scanner inaccuracies to clawhub-clawscan.

    Also in 3 projects that use this

  • openclawSkill

    clawhub-clawscan

    Investigate ClawScan health alerts, diagnose scanner or ClawHub integration failures, prepare operational repairs, and route validated verdict inaccuracies upstream. Account ban appeals use clawhub-ban-appeals.

    Also in 3 projects that use this

  • FabienGreardSkill

    convex-acquire-domain

    Find and buy a domain for the current Convex app through Convex, then bind it (labs; spend action).

    Also in 3 projects that use this

  • +9 more from openclaw

The file

We split this skill into the rules it gives an agent

5 rules. 4 are common, 1 is distinctive, and 0 are very specific to this project.

  • 4

    Common

    Most of the 15 edited versions we read keep these.

  • 1

    Distinctive

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Do not assume a provider. Before writing setup code:

    line 19
  2. Common:

    Ask the user which auth solution they want, unless the repository already makes it obvious

    line 21
  3. Common:

    If the repo already uses a provider, continue with that provider unless the user wants to switch

    line 22
  4. Common:

    If the user has not chosen and the repo does not make it obvious, ask before proceeding

    line 23
  5. Common:

    Look for signals in the repo before asking:

    line 33

“Common” is measured: kept by at least 6 in 10 of the 15 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Convex Authentication Setup

Implement secure authentication in Convex with user management and access control.

When to Use

  • Setting up authentication for the first time
  • Implementing user management (users table, identity mapping)
  • Creating authentication helper functions
  • Setting up auth providers (Convex Auth, Clerk, WorkOS AuthKit, Auth0, custom JWT)

First Step: Choose the Auth Provider

Do not assume a provider. Before writing setup code:

  1. Ask the user which auth solution they want, unless the repository already makes it obvious
  2. If the repo already uses a provider, continue with that provider unless the user wants to switch
  3. If the user has not chosen and the repo does not make it obvious, ask before proceeding

Common options:

  • Convex Auth: good default when the user wants auth handled directly in Convex
  • Clerk: use when the app already uses Clerk
  • WorkOS AuthKit: use when the app already uses WorkOS
  • Auth0: use when the app already uses Auth0
  • Custom JWT provider: use when integrating an existing auth system

Look for signals in the repo before asking:

  • Dependencies such as @clerk/*, @workos-inc/*, @auth0/*
  • Existing files such as convex/auth.config.ts, auth middleware, provider wrappers
  • Environment variables that clearly point at a provider

Schema Setup

// convex/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";

export default defineSchema({
  users: defineTable({
    tokenIdentifier: v.string(),
    name: v.string(),
    email: v.string(),
    pictureUrl: v.optional(v.string()),
    role: v.union(v.literal("user"), v.literal("admin")),
    createdAt: v.number(),
    updatedAt: v.optional(v.number()),
  })
    .index("by_token", ["tokenIdentifier"])
    .index("by_email", ["email"]),
});

Core Helper Functions

Get Current User

// convex/lib/auth.ts
import { QueryCtx, MutationCtx } from "./_generated/server";
import { Doc } from "./_generated/dataModel";

export async function getCurrentUser(
  ctx: QueryCtx | MutationCtx
): Promise<Doc<"users">> {
  const identity = await ctx.auth.getUserIdentity();
  if (!identity) throw new Error("Not authenticated");

  const user = await ctx.db
    .query("users")
    .withIndex("by_token", q =>
      q.eq("tokenIdentifier", identity.tokenIdentifier)
    )
    .unique();

  if (!user) throw new Error("User not found");
  return user;
}

export async function getCurrentUserOrNull(
  ctx: QueryCtx | MutationCtx
): Promise<Doc<"users"> | null> {
  const identity = await ctx.auth.getUserIdentity();
  if (!identity) return null;

  return await ctx.db
    .query("users")
    .withIndex("by_token", q =>
      q.eq("tokenIdentifier", identity.tokenIdentifier)
    )
    .unique();
}

export async function requireAdmin(
  ctx: QueryCtx | MutationCtx
): Promise<Doc<"users">> {
  const user = await getCurrentUser(ctx);
  if (user.role !== "admin") throw new Error("Admin access required");
  return user;
}

User Creation/Upsert

// convex/users.ts
import { mutation } from "./_generated/server";
import { v } from "convex/values";

export const storeUser = mutation({
  args: {},
  handler: async (ctx) => {
    const identity = await ctx.auth.getUserIdentity();
    if (!identity) throw new Error("Not authenticated");

    const existingUser = await ctx.db
      .query("users")
      .withIndex("by_token", q =>
        q.eq("tokenIdentifier", identity.tokenIdentifier)
      )
      .unique();

    if (existingUser) {
      await ctx.db.patch(existingUser._id, { updatedAt: Date.now() });
      return existingUser._id;
    }

    return await ctx.db.insert("users", {
      tokenIdentifier: identity.tokenIdentifier,
      name: identity.name ?? "Anonymous",
      email: identity.email ?? "",
      pictureUrl: identity.pictureUrl,
      role: "user",
      createdAt: Date.now(),
    });
  },
});

Access Control Patterns

Resource Ownership

export const deleteTask = mutation({
  args: { taskId: v.id("tasks") },
  handler: async (ctx, args) => {
    const user = await getCurrentUser(ctx);
    const task = await ctx.db.get(args.taskId);
    if (!task) throw new Error("Task not found");
    if (task.userId !== user._id) throw new Error("You can only delete your own tasks");
    await ctx.db.delete(args.taskId);
  },
});

Team-Based Access

async function requireTeamAccess(
  ctx: MutationCtx,
  teamId: Id<"teams">
): Promise<{ user: Doc<"users">, membership: Doc<"teamMembers"> }> {
  const user = await getCurrentUser(ctx);
  const membership = await ctx.db
    .query("teamMembers")
    .withIndex("by_team_and_user", q =>
      q.eq("teamId", teamId).eq("userId", user._id)
    )
    .unique();

  if (!membership) throw new Error("You don't have access to this team");
  return { user, membership };
}

Checklist

  • Chosen the correct auth provider before writing setup code
  • Users table with tokenIdentifier index
  • getCurrentUser helper function
  • storeUser mutation for first sign-in
  • Authentication check in all protected functions
  • Authorization check for resource access
  • Clear error messages ("Not authenticated", "Unauthorized")
  • Client auth provider configured

Source: https://github.com/get-convex/agent-skills

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.