feat(admin): implement admin auth & authorization system (Phases 0–6)
Complete implementation of the admin authentication and authorization plan using a separate Clerk instance (App B) for cryptographic isolation from the storefront. Convex backend changes: - auth.config.ts: dual JWT provider (storefront + admin Clerk issuers) - http.ts: add /clerk-admin-webhook route with separate signing secret - users.ts: role-aware upsertFromClerk (optional role arg), store reads publicMetadata.role from JWT, assertSuperAdmin internal query - model/users.ts: add requireSuperAdmin helper - adminInvitations.ts: inviteAdmin action (super_admin gated, Clerk Backend SDK) Admin app (apps/admin): - Route groups: (auth) for sign-in, (dashboard) for gated pages - AdminUserSync, AdminAuthGate, AccessDenied, LoadingSkeleton components - useAdminAuth hook with loading/authorized/denied state machine - RequireRole component for super_admin-only UI sections - useStoreUserEffect hook for Clerk→Convex user sync - Sidebar shell with nav-main, nav-user, app-sidebar - clerkMiddleware with /sign-in excluded from auth.protect - ShadCN UI components (sidebar, dropdown, avatar, etc.) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
26
convex/adminInvitations.ts
Normal file
26
convex/adminInvitations.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
"use node";
|
||||
|
||||
import { v } from "convex/values";
|
||||
import { action } from "./_generated/server";
|
||||
import { internal } from "./_generated/api";
|
||||
import { createClerkClient } from "@clerk/backend";
|
||||
|
||||
export const inviteAdmin = action({
|
||||
args: {
|
||||
email: v.string(),
|
||||
role: v.union(v.literal("admin"), v.literal("super_admin")),
|
||||
},
|
||||
handler: async (ctx, { email, role }) => {
|
||||
await ctx.runQuery(internal.users.assertSuperAdmin);
|
||||
|
||||
const clerk = createClerkClient({
|
||||
secretKey: process.env.CLERK_ADMIN_SECRET_KEY!,
|
||||
});
|
||||
|
||||
await clerk.invitations.createInvitation({
|
||||
emailAddress: email,
|
||||
redirectUrl: `${process.env.ADMIN_URL}/sign-in`,
|
||||
publicMetadata: { role },
|
||||
});
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user