.claude/skills/convex-setup-auth/SKILL.md
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.
npx skillsauth add get-convex/components-submissions-directory convex-setup-authInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Implement secure authentication in Convex with user management and access control.
Do not assume a provider. Before writing setup code:
Common options:
Look for signals in the repo before asking:
@clerk/*, @workos-inc/*, @auth0/*convex/auth.config.ts, auth middleware, provider wrappers// 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"]),
});
// 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;
}
// 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(),
});
},
});
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);
},
});
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 };
}
tokenIdentifier indexgetCurrentUser helper functionstoreUser mutation for first sign-inSource: https://github.com/get-convex/agent-skills
development
Debug and troubleshoot WorkOS AuthKit authentication issues with Convex. Use when authentication fails, JWT validation errors occur, user identity returns null, email claims are missing, admin access checks fail, or sign in button does not work. Supports Netlify deployment.
development
Set up and configure WorkOS AuthKit authentication with Convex backend. Use when integrating AuthKit, configuring JWT providers, setting up environment variables, or implementing sign in and sign out flows with React and Vite. Supports Netlify deployment.
documentation
# Update project docs Use this skill after completing any feature, fix, or migration to keep the three core project tracking files in sync. Activate with: `@update-project-docs` ## Step 1: Get real dates Run this first: ```bash git log --date=short -n 10 ``` Use actual commit dates. Never use placeholder dates or future months. ## Step 2: Update TASK.md Move completed items into `## Completed` with date and time: ```markdown - [x] Feature name (YYYY-MM-DD HH:mm UTC) - [x] Sub-task det
tools
# Create a PRD Use this skill before any multi-file feature, architectural decision, or complex bug fix. Activate with: `@create-prd` ## Location and naming - All PRDs live in `prds/` folder - File name: `prds/<feature-or-problem-slug>.md` - Extension is always `.md`, not `.prd` - Use kebab-case for the filename (e.g., `prds/adding-email-auth.md`) ## Template Copy and fill in this template: ```markdown # [Feature or problem name] Created: YYYY-MM-DD HH:mm UTC Last Updated: YYYY-MM-DD HH: