Skip to main content

Authentication overview

Covara ships a complete authentication and authorization stack. There are two ways to authenticate users, and one consistent way to authorize them.

Just want it working?

Follow the Auth quickstart — the fastest path to email/password (with email confirmation) plus GitHub social login, and the one table you actually need to create.

Two authentication approaches

ApproachUse whenPage
OIDC providerYou want a standards-based identity server (OAuth2/OIDC, PKCE, federated login, JWT access tokens) for one or many apps.OIDC provider
Session-based useAuthYou want classic email/password sessions with cookies, the fastest path to login/signup/logout.Sessions

Both populate the same request context, so authorization scopes, subscriptions, and the client useAuth hook work identically regardless of which you choose.

On top of either approach you can layer: social login (sign in with GitHub/Discord/Google/… via any Passport.js strategy), JWT tokens, federated login, API keys, MFA/TOTP, magic links, a password policy, and account-security flows (CSRF, login throttling, email verification, password reset).

The request user

After auth middleware runs, the authenticated user is available in any Hono handler:

import { getUser, requireUser, getSession } from "covara";

app.get("/api/profile", (c) => {
const user = requireUser(c); // throws 401 if absent
return c.json(user);
});

getUser(c) returns the user or null; requireUser(c) throws an UnauthorizedError; getSession(c) returns the session. These read from Hono's typed ContextVariableMap (user, session, requestId, apiVersion).

Quick session setup

useAuth decouples how the identity is persisted (a session strategycookieSession or jwtSession) from who the user is (credential providers — login, signup, social, …), so any provider composes with any session type.

import { createCovara, cookieSession, useAuth, hashPassword, verifyPassword } from "covara";
import { eq } from "drizzle-orm";

const auth = useAuth({
// swap for jwtSession({ secret, getUserById }) to issue JWTs instead
session: cookieSession({
getUserById: async (id) => db.query.users.findFirst({ where: eq(users.id, id) }),
}),
login: {
validateCredentials: async (email, password) => {
const user = await db.query.users.findFirst({ where: eq(users.email, email) });
return user && (await verifyPassword(password, user.passwordHash))
? { id: user.id, email: user.email, name: user.name }
: null;
},
},
signup: {
createUser: async ({ email, password, name }) => {
const [u] = await db.insert(users)
.values({ id: crypto.randomUUID(), email, name, passwordHash: await hashPassword(password) })
.returning();
return { id: u.id, email: u.email, name: u.name };
},
},
});

const app = createCovara({ auth }); // mounts /api/auth/* and the middleware

This creates four routes:

RouteMethodDescription
/api/auth/meGETCurrent user, or { user: null }
/api/auth/loginPOSTEmail/password login
/api/auth/signupPOSTCreate account
/api/auth/logoutPOSTClear session

Full options and adapters: Sessions.

Route guards

import { requireAuth, requireRole, requirePermission, getUser } from "covara";

app.get("/profile", requireAuth(), (c) => c.json(getUser(c)));
app.get("/admin", requireRole("admin"), (c) => c.json({ ok: true }));
app.post("/posts", requirePermission("posts:create"), async (c) => { /* ... */ });

Authorization

Authentication answers who is this; authorization answers what can they touch. Covara enforces row-level access with RSQL scopes on every read, write, subscription, and search — combined with the request filter and impossible to bypass from the client. See Authorization scopes and Secure queries.

Client-side useAuth

The React useAuth hook exposes the auth state and supports several strategies — cookie sessions, JWT, bearer token, API key, or auto-detect:

import { useAuth } from "covara/client/react";

function App() {
const { user, isAuthenticated, isLoading, logout } = useAuth<User>();
if (isLoading) return <div>Loading…</div>;
if (!isAuthenticated) return <LoginPage />;
return <button onClick={logout}>Sign out {user?.name}</button>;
}

See Client auth for every strategy and the OIDC PKCE flow.