convex-auth — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited convex-auth (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
This skill covers universal auth patterns that work with ANY authentication provider.
Access the authenticated user via ctx.auth.getUserIdentity():
import { query, mutation, action } from './_generated/server';
export const myQuery = query({
args: {},
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity();
if (identity === null) {
throw new Error('Not authenticated');
}
// identity.tokenIdentifier - unique across providers
// identity.subject - user ID from provider
// identity.email, identity.name, etc. (if configured)
}
});| Field | Guaranteed | Description |
|---|---|---|
tokenIdentifier | ✅ | Unique ID (subject + issuer combined) |
subject | ✅ | User ID from auth provider |
issuer | ✅ | Auth provider domain |
email | Provider-dependent | User's email |
name | Provider-dependent | Display name |
pictureUrl | Provider-dependent | Avatar URL |
Problem: Queries can execute before auth is validated on page load.
Client-side: Always use useConvexAuth() from convex/react, NOT your provider's hook:
// ✅ Correct - waits for Convex to validate token
import { useConvexAuth } from 'convex/react';
const { isLoading, isAuthenticated } = useConvexAuth();
// ❌ Wrong - only checks provider, not Convex validation
const { isSignedIn } = useAuth(); // from @clerk/clerk-reactSkip queries until authenticated:
const user = useQuery(api.users.current, isAuthenticated ? {} : 'skip');Use Convex auth components:
import { Authenticated, Unauthenticated, AuthLoading } from "convex/react";
<Authenticated>
<Content /> {/* Queries here are safe */}
</Authenticated>
<Unauthenticated>
<SignInButton />
</Unauthenticated>See USERS.md for complete patterns including:
Create authenticated function wrappers using convex-helpers:
// convex/lib/auth.ts
import {
query,
mutation,
action,
QueryCtx,
MutationCtx,
ActionCtx
} from './_generated/server';
import {
customQuery,
customMutation,
customAction,
customCtx
} from 'convex-helpers/server/customFunctions';
import { ConvexError } from 'convex/values';
async function requireAuth(ctx: QueryCtx | MutationCtx | ActionCtx) {
const identity = await ctx.auth.getUserIdentity();
if (!identity) {
throw new ConvexError('Not authenticated');
}
return identity;
}
export const authQuery = customQuery(
query,
customCtx(async (ctx) => {
const identity = await requireAuth(ctx);
return { identity };
})
);
export const authMutation = customMutation(
mutation,
customCtx(async (ctx) => {
const identity = await requireAuth(ctx);
return { identity };
})
);
export const authAction = customAction(
action,
customCtx(async (ctx) => {
const identity = await requireAuth(ctx);
return { identity };
})
);Usage:
import { authQuery } from './lib/auth';
export const myProtectedQuery = authQuery({
args: {},
handler: async (ctx) => {
// ctx.identity is guaranteed to exist
const userId = ctx.identity.subject;
}
});Pass JWT in Authorization header:
// Client
fetch('https://your-deployment.convex.site/api/endpoint', {
headers: { Authorization: `Bearer ${jwtToken}` }
});
// convex/http.ts
import { httpAction } from './_generated/server';
export const myEndpoint = httpAction(async (ctx, request) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) {
return new Response('Unauthorized', { status: 401 });
}
// ...
});See DEBUG.md for detailed troubleshooting steps.
Quick checks:
ctx.auth.getUserIdentity() returns null?"skip" pattern)npx convex dev)iss and aud fieldsdomain in auth.config.ts matches JWT issapplicationID matches JWT audctx.auth.getUserIdentity() in public functionsuseConvexAuth() hook, not provider's auth hook"skip" until authenticated<Authenticated> component to gate protected contenttokenIdentifier or subject as unique user keyisAuthenticated.filter() to find users by token (use index)~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.