Platform Operators
Shared Package
Types, constants, validation utilities, ID generation, and error classes shared across all Banata Auth packages.
@banata-auth/shared is the foundation package for the entire Banata Auth ecosystem. It defines the TypeScript interfaces, constants, validation schemas, ID generation utilities, and error classes that every other package depends on -- @banata-auth/sdk, @banata-auth/convex, @banata-auth/react, and the dashboard all import from here.
If you are working on Banata internals, contributing a plugin, or building tooling on top of the platform, this is the package you will reach for most often.
Installation
npm install @banata-auth/sharedThe only runtime dependency is zod (used for validation schemas).
Package Structure
The shared package exports six modules, all re-exported from the package root:
| Module | What it provides |
|---|---|
types | TypeScript interfaces for every Banata Auth resource |
constants | ID_PREFIXES, RATE_LIMITS, TOKEN_LIFETIMES, webhook tuning values |
id | generateId, ulid, getResourceType, validateId, generateRandomToken, generateOtp |
validation | Zod schemas for input validation |
errors | BanataAuthError base class and specialized subclasses |
email-blocks | Block type system, template definitions, factory helpers |
You can import anything directly from the package root:
import {
ID_PREFIXES,
generateId,
BanataAuthError,
emailSchema,
} from "@banata-auth/shared";TypeScript Interfaces
User
The central identity record. Every authenticated person in a Banata project has one.
interface User {
id: string;
email: string;
emailVerified: boolean;
name: string;
image: string | null;
username: string | null;
role: "user" | "admin";
banned: boolean;
twoFactorEnabled: boolean;
metadata: Record<string, unknown> | null;
createdAt: Date;
updatedAt: Date;
}Session
Represents an active login. Sessions are tied to a user and optionally scoped to an organization.
interface Session {
id: string;
userId: string;
token: string;
expiresAt: Date;
ipAddress: string | null;
userAgent: string | null;
activeOrganizationId: string | null;
impersonatedBy: string | null;
createdAt: Date;
updatedAt: Date;
}Account
Links a user to an authentication provider (email/password, GitHub, Google, SSO, etc.).
interface Account {
id: string;
userId: string;
accountId: string;
providerId: string;
createdAt: Date;
updatedAt: Date;
}Organization
A workspace that groups users together. Organizations can enforce MFA, restrict email domains, and enable SSO.
interface Organization {
id: string;
name: string;
slug: string;
logo: string | null;
metadata: Record<string, unknown> | null;
requireMfa: boolean;
ssoEnforced: boolean;
allowedEmailDomains: string[] | null;
maxMembers: number | null;
createdAt: Date;
updatedAt: Date;
}OrganizationMember
Tracks a user's membership and role within an organization.
interface OrganizationMember {
id: string;
organizationId: string;
userId: string;
role: string;
source: "manual" | "invitation" | "sso" | "scim" | "api";
teamIds: string[];
createdAt: Date;
updatedAt: Date;
}SSO and Directory Types
SSO connections (SsoConnection) and directory sync resources (Directory, DirectoryUser, DirectoryGroup) follow the same pattern. Key type aliases you will encounter:
type SsoConnectionType = "saml" | "oidc";
type SsoConnectionState = "draft" | "active" | "inactive" | "validating";
type DirectoryProvider =
| "okta" | "azure_scim_v2" | "google_workspace"
| "onelogin" | "jumpcloud" | "pingfederate"
| "generic_scim_v2";
type DirectoryState = "linked" | "unlinked" | "invalid_credentials";Other Resources
The shared package also defines interfaces for OrganizationInvitation, Team, SsoProfile, AuditEvent, WebhookEndpoint, ApiKey, VaultSecret, DomainVerification, and Project. Each follows the same convention: a prefixed id, resource-specific fields, and createdAt / updatedAt timestamps.
Constants
ID Prefixes
Every Banata Auth resource uses a prefixed ULID as its identifier. The prefix makes IDs self-documenting -- you can tell at a glance what kind of resource you are looking at.
import { ID_PREFIXES } from "@banata-auth/shared";
// A selection of commonly used prefixes:
// user -> "usr"
// session -> "ses"
// account -> "acc"
// organization -> "org"
// organizationMember -> "mem"
// organizationInvitation -> "inv"
// team -> "team"
// ssoConnection -> "conn"
// directory -> "dir"
// auditEvent -> "evt"
// webhookEndpoint -> "wh"
// apiKey -> "ak"
// project -> "proj"The full map is exported as ID_PREFIXES and covers every resource type in the system.
Rate Limits
Default rate limits per endpoint category, expressed as requests per minute:
import { RATE_LIMITS } from "@banata-auth/shared";
RATE_LIMITS.general; // 600
RATE_LIMITS.signIn; // 30
RATE_LIMITS.signUp; // 10
RATE_LIMITS.passwordReset; // 10
RATE_LIMITS.emailOperations; // 10
RATE_LIMITS.scim; // 100
RATE_LIMITS.admin; // 120
RATE_LIMITS.webhookDelivery; // 0 (no limit -- outbound)Token Lifetimes
Default lifetimes for tokens and sessions, in seconds:
import { TOKEN_LIFETIMES } from "@banata-auth/shared";
TOKEN_LIFETIMES.accessToken; // 900 (15 min)
TOKEN_LIFETIMES.session; // 604800 (7 days)
TOKEN_LIFETIMES.sessionAbsoluteMax; // 2592000 (30 days)
TOKEN_LIFETIMES.passwordReset; // 3600 (1 hour)
TOKEN_LIFETIMES.emailVerification; // 86400 (24 hours)
TOKEN_LIFETIMES.magicLink; // 600 (10 min)
TOKEN_LIFETIMES.emailOtp; // 600 (10 min)
TOKEN_LIFETIMES.invitation; // 604800 (7 days)
TOKEN_LIFETIMES.jwksRotation; // 7776000 (90 days)Webhook Constants
Webhook retry behavior is controlled by three constants:
import {
WEBHOOK_RETRY_DELAYS,
WEBHOOK_MAX_CONSECUTIVE_FAILURES,
WEBHOOK_MAX_ATTEMPTS,
} from "@banata-auth/shared";
WEBHOOK_RETRY_DELAYS; // [0, 300000, 1800000, 7200000, 86400000]
WEBHOOK_MAX_CONSECUTIVE_FAILURES; // 3 (auto-disable after 3 failures)
WEBHOOK_MAX_ATTEMPTS; // 5ID Generation
generateId
Generate a prefixed ULID for any resource type:
import { generateId } from "@banata-auth/shared";
generateId("user"); // "usr_01H9GBQN5WP3FVJKZ0JGMH3RXE"
generateId("organization"); // "org_01H9GBQN5WP3FVJKZ0JGMH3RXE"
generateId("ssoConnection"); // "conn_01H9GBQN5WP3FVJKZ0JGMH3RXE"IDs are ULID-compatible: the first 10 characters encode a millisecond timestamp (giving you lexicographic sort order for free), and the remaining 16 characters are cryptographically random.
ulid
Generate a raw ULID without a prefix:
import { ulid } from "@banata-auth/shared";
ulid(); // "01H9GBQN5WP3FVJKZ0JGMH3RXE"getResourceType
Extract the resource type from a prefixed ID:
import { getResourceType } from "@banata-auth/shared";
getResourceType("usr_01H9GBQN..."); // "user"
getResourceType("org_01H9GBQN..."); // "organization"
getResourceType("invalid"); // nullvalidateId
Check whether an ID has the correct prefix for a given resource type:
import { validateId } from "@banata-auth/shared";
validateId("usr_01H9...", "user"); // true
validateId("org_01H9...", "user"); // false
validateId("conn_01H9...", "ssoConnection"); // truegenerateRandomToken
Generate a URL-safe base64 random token (useful for password-reset links, magic links, etc.):
import { generateRandomToken } from "@banata-auth/shared";
generateRandomToken(); // 32 bytes (default), URL-safe base64
generateRandomToken(48); // 48 bytesgenerateOtp
Generate a random numeric one-time password:
import { generateOtp } from "@banata-auth/shared";
generateOtp(); // "482917" (6 digits, default)
generateOtp(8); // "38291047" (8 digits)Validation Schemas
All validation schemas are built with Zod. They are used throughout the backend for input validation, and you can use them in your own code for form validation or API contracts.
Primitive Schemas
import {
emailSchema,
passwordSchema,
nameSchema,
slugSchema,
urlSchema,
httpsUrlSchema,
domainSchema,
metadataSchema,
} from "@banata-auth/shared";
emailSchema.parse("user@example.com"); // valid
passwordSchema.parse("mypassword123"); // valid (8-128 chars)
slugSchema.parse("my-organization"); // valid (lowercase, alphanumeric + hyphens)
httpsUrlSchema.parse("https://..."); // valid (HTTPS required)Resource Schemas
Each resource has a create and update schema, plus a corresponding TypeScript input type:
import {
createUserSchema,
updateUserSchema,
createOrganizationSchema,
paginationSchema,
} from "@banata-auth/shared";
import type {
CreateUserInput,
UpdateUserInput,
PaginationOptions,
} from "@banata-auth/shared";
const input = createUserSchema.parse({
email: "jane@example.com",
name: "Jane Doe",
password: "securepassword123",
role: "user",
});Error Classes
All Banata Auth errors extend BanataAuthError, which provides structured error information with HTTP status codes, machine-readable codes, and request IDs.
class BanataAuthError extends Error {
readonly status: number;
readonly code: string;
readonly requestId: string;
readonly retryable: boolean;
toJSON(): object;
}Specialized Errors
| Class | Status | Code | Retryable | When you will see it |
|---|---|---|---|---|
AuthenticationError | 401 | authentication_required | No | Missing or invalid credentials |
ForbiddenError | 403 | forbidden | No | Valid credentials, insufficient permissions |
NotFoundError | 404 | not_found | No | Resource does not exist |
ConflictError | 409 | conflict | No | Duplicate resource |
ValidationError | 422 | validation_error | No | Input validation failed (includes errors: FieldError[]) |
RateLimitError | 429 | rate_limit_exceeded | Yes | Rate limit exceeded (includes retryAfter: number) |
InternalError | 500 | internal_error | Yes | Server-side error |
Usage
import {
BanataAuthError,
ValidationError,
NotFoundError,
} from "@banata-auth/shared";
try {
await someOperation();
} catch (err) {
if (err instanceof ValidationError) {
for (const fieldError of err.errors) {
console.log(`${fieldError.field}: ${fieldError.message}`);
}
} else if (err instanceof NotFoundError) {
console.log("Resource not found");
} else if (err instanceof BanataAuthError && err.retryable) {
// safe to retry
}
}Email Block System
The shared package defines the block type system used by the email template editor, SDK, and backend renderer. Block types include heading, text, button, image, divider, spacer, link, code, and columns.
You will also find factory helpers (createDefaultBlock), variable interpolation (interpolateVariables, extractVariables), and built-in template block definitions (getBuiltInTemplateBlocks).
For the full guide on customizing email templates, see Email Templates.
Pagination Types
All list endpoints use cursor-based pagination with a consistent shape:
import type { PaginatedResult, ListMetadata } from "@banata-auth/shared";
interface PaginatedResult<T> {
data: T[];
listMetadata: ListMetadata;
}
interface ListMetadata {
before: string | null; // cursor for the previous page
after: string | null; // cursor for the next page
}Next Steps
- Convex Integration -- the platform runtime that uses these shared definitions
- SDK Reference -- the admin SDK that consumes these types
- Email Templates -- full guide to the block-based email system
- Vault and Encryption -- encryption layer using shared VaultSecret types
- Project Structure -- how the monorepo packages fit together