Security model
Understand the trust boundaries and application responsibilities around Better Convex Nuxt.
Better Convex Nuxt transports identity; it does not replace application authorization. Every protected Convex function must verify the caller and enforce access to the requested resource.
Trust boundaries
| Boundary | Responsibility |
|---|---|
| Nuxt UI | Display state and capabilities; never final authorization |
| Nuxt server | Request-scoped rendering, server routes, and the bounded same-origin auth proxy |
| Better Auth | Sessions, sign-in, sign-out, and account recovery |
| Convex token exchange | Turn the checked session into a Convex session token |
| Convex functions | Access checks, tenant isolation, validation, and bounded data access |
Route middleware improves navigation UX. Hiding a button improves UX. Neither protects data.
Use auth.requireUser(ctx) or auth.getUser(ctx) from createBetterConvexAuth.
They accept only a Convex session token whose Better Auth session still exists.
When Better Auth revokes all sessions of a user, for example on a password
reset with revokeSessionsOnPasswordReset, every older token stops working at
once.
ctx.auth.getUserIdentity() alone does not check revocation.
export const remove = mutation({
args: { projectId: v.id('projects') },
handler: async (ctx, { projectId }) => {
const user = await auth.requireUser(ctx)
const project = await ctx.db.get(projectId)
if (!project || project.ownerId !== user.id) {
throw new ConvexError({ code: 'FORBIDDEN' })
}
await ctx.db.delete(projectId)
},
})Auth proxy contract
The module exposes one /api/auth proxy. Sign-in, session, consent, administration, authorize, revoke, and plugin routes accept same-origin requests only. It accepts only the supported Better Auth cookie namespace, GET and POST requests, bounded bodies, and a validated upstream origin. It does not follow upstream redirects with credentials attached.
The narrow browser OAuth exception is the exact public-client form token route:
cross-origin POST /api/auth/oauth2/token and its exact OPTIONS preflight,
without a query, Cookie, Authorization, proxy authorization, DPoP, or
credentialed CORS. Its form body is independently bounded, upstream CORS headers
cannot widen the response, and a token response that tries to set a cookie is
rejected. The public authorization-server and protected-resource metadata
documents use wildcard non-credentialed CORS because they contain no secret or
user state.
Do not widen its cookie allowlist, add caller-controlled forwarding headers, or introduce a second token-exchange path. If a Better Auth plugin requires cookies outside the supported namespace, it is not compatible with this boundary.
Browser risk
The browser holds a Convex session token so Convex subscriptions can authenticate. An XSS flaw or a compromised script on your origin can act as the user and copy that token until it expires, after at most 15 minutes. Use safe Vue rendering, a strict Content Security Policy, and few third-party scripts. High-risk operations should load the current state again in Convex before they change anything.
Operator responsibilities
You are responsible for TLS, host validation, secret storage, OAuth provider settings, recovery email, CSP, log access, dependency updates, access rules, backups, and incident response. The repository security policy lists what the library protects and the known remaining risks.
If a Better Auth secret, the proxy signing secret, a session, an OAuth client, a consent or token, a signing key, or a package may be compromised, follow the policy's incident steps.
Report suspected vulnerabilities through the private channel described in that policy, not a public issue.