
Convex Setup Auth
- 3 installs
- 627 repo stars
- Updated May 20, 2026
- waynesutton/markdown-site
Sets up Convex authentication with user management, identity mapping, and access control, choosing among Convex Auth, Clerk, WorkOS, Auth0, or custom JWT.
About
This skill implements secure Convex authentication with a users table, identity mapping, auth helpers, and role-based access control. A developer uses it when adding auth for the first time, wiring OAuth providers, or adding access control to a Convex app.
- Requires choosing the auth provider before writing setup code
- Covers user management, identity mapping, and role-based access
Convex Setup Auth by the numbers
- 3 all-time installs (skills.sh)
- +1 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #3,739 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/waynesutton/markdown-site --skill convex-setup-authAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3 |
|---|---|
| repo stars | ★ 627 |
| Last updated | May 20, 2026 |
| Repository | waynesutton/markdown-site ↗ |
What it does
Sets up Convex authentication with user management, identity mapping, and access control, choosing among Convex Auth, Clerk, WorkOS, Auth0, or custom JWT.
Files
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
tokenIdentifierindex - [ ]
getCurrentUserhelper function - [ ]
storeUsermutation 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