Docs/Authentication & Security
Stable

Authentication & Security

Sessions, users, roles & permissions (RBAC), and crypto ports built into the framework.

3 min readVeap Core TeamUpdated 2026-03

Veap ships a complete session-based authentication system in @veap/core. It covers sign-in/sign-up, sessions with server-side validation, email verification, password reset with recovery codes, RBAC (roles and permissions), and extension points that 2FA plugins use.

Mental model#

  • Identity: users, roles, permissions tables with pivot tables users_to_roles, users_to_permissions, roles_to_permissions.
  • Sessions: opaque random tokens (Oslo base32) stored server-side; the raw token lives in a session cookie, the database stores its SHA-256-style hash. Validation hashes the cookie value and looks it up, checking expiry.
  • Facades: server-action wrappers over six application services (AuthService, SessionService, UserService, RbacService, PasswordResetService, EmailVerificationService), bound in AuthServiceProvider and exposed from @veap/core/auth/server.
  • Crypto ports: password hashing (IPasswordHasher, bcrypt adapter), token generation (ITokenGenerator, Oslo adapter) and secret-at-rest encryption (ISecretCipher, AES-GCM adapter with ENCRYPTION_KEY) are injectable ports.

Where things live#

ConcernAPI (all server-side unless noted)
Current sessiongetCurrentSession()
Login / signup / logoutsignIn, signUp, signOut, finalizeLogin
Session managementgenerateSessionToken, createSession, invalidateSession, invalidateUserSessions, getUserSessions, invalidateOtherSessions, updateSessionMetadata
Cookie helperssetSessionTokenCookie, deleteSessionTokenCookie
UserscreateUser, createOAuthUser, getUserById, getUserFromEmail, updateUserPassword, updateUserName, updateUserAwatar (sic, kept for compatibility), recovery code helpers
Email verificationinitEmailVerification, createEmailVerificationRequest, sendVerificationEmail, getUserEmailVerificationRequestFromRequest, cookie helpers
Password resetcreatePasswordResetSession, validatePasswordResetSessionToken, getCurrentPasswordResetSession, sendPasswordResetEmail, cookie helpers
RBACgetRoles, createRole, getPermissions, createPermission, assignPermissionToRole, assignRoleToUser, ...
Security checkscheckSecurity(session, user, roles, permissions)
Validation schemas (client-safe)loginSchema, registerSchema, forgotPasswordSchema, resetPasswordSchema, verifyEmailSchema, ...
Types (client-safe)User, Session, AuthSession, FullUser, AuthResponse

All of these come from @veap/core/auth/server unless marked client-safe (schemas and types also from @veap/core/auth).

Reading the current session#

tsx
// Server Component
import { getCurrentSession } from "@veap/core/auth/server";

export default async function Header() {
  const { user, session } = await getCurrentSession();
  if (!user) return <a href="/signin">Sign in</a>;
  return <p>Signed in as {user.name}</p>;
}

getCurrentSession() is React-cached: one lookup per request no matter how many components call it. The returned user is a FullUser: the base record plus roles, permissions and any data added by registered augmenters. Sensitive fields (password, recovery code) are stripped before serialization.

Signing in and out#

Use the built-in actions from a client component:

tsx
"use client";

import { signIn } from "@veap/core/auth/server";
import { loginSchema, type LoginInput } from "@veap/core/auth";

export function SignInForm() {
  async function onSubmit(formData: FormData) {
    const input = {
      email: String(formData.get("email")),
      password: String(formData.get("password")),
    } satisfies LoginInput;

    const parsed = loginSchema.safeParse(input);
    if (!parsed.success) return;

    const result = await signIn(parsed.data);
    if (result.success) {
      // result.data: AuthResponse
      // status SUCCESS -> session established; CHALLENGE_REQUIRED -> 2FA flow
    }
  }

  return (
    <form action={onSubmit}>
      <input name="email" type="email" />
      <input name="password" type="password" />
      <button type="submit">Sign in</button>
    </form>
  );
}

AuthResponse is a discriminated union: SUCCESS (with session and user), CHALLENGE_REQUIRED (2FA plugins intercept here and redirect into their own flow), or ERROR (message). Signing out is signOut().

Auth events#

Authentication publishes on the event bus: system:auth:login, system:auth:signup, system:auth:session-created, system:auth:signed-out, plus verification/reset events. Payloads carry the Session and User. Subscribe in a plugin's init().

In-depth pages#

veap
Preparing0%