Banata

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

bash
npm install @banata-auth/shared

The only runtime dependency is zod (used for validation schemas).


Package Structure

The shared package exports six modules, all re-exported from the package root:

ModuleWhat it provides
typesTypeScript interfaces for every Banata Auth resource
constantsID_PREFIXES, RATE_LIMITS, TOKEN_LIFETIMES, webhook tuning values
idgenerateId, ulid, getResourceType, validateId, generateRandomToken, generateOtp
validationZod schemas for input validation
errorsBanataAuthError base class and specialized subclasses
email-blocksBlock type system, template definitions, factory helpers

You can import anything directly from the package root:

typescript
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.

typescript
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.

typescript
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.).

typescript
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.

typescript
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.

typescript
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:

typescript
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.

typescript
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:

typescript
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:

typescript
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:

typescript
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;               // 5

ID Generation

generateId

Generate a prefixed ULID for any resource type:

typescript
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:

typescript
import { ulid } from "@banata-auth/shared";
 
ulid(); // "01H9GBQN5WP3FVJKZ0JGMH3RXE"

getResourceType

Extract the resource type from a prefixed ID:

typescript
import { getResourceType } from "@banata-auth/shared";
 
getResourceType("usr_01H9GBQN...");  // "user"
getResourceType("org_01H9GBQN...");  // "organization"
getResourceType("invalid");          // null

validateId

Check whether an ID has the correct prefix for a given resource type:

typescript
import { validateId } from "@banata-auth/shared";
 
validateId("usr_01H9...", "user");           // true
validateId("org_01H9...", "user");           // false
validateId("conn_01H9...", "ssoConnection"); // true

generateRandomToken

Generate a URL-safe base64 random token (useful for password-reset links, magic links, etc.):

typescript
import { generateRandomToken } from "@banata-auth/shared";
 
generateRandomToken();    // 32 bytes (default), URL-safe base64
generateRandomToken(48);  // 48 bytes

generateOtp

Generate a random numeric one-time password:

typescript
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

typescript
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:

typescript
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.

typescript
class BanataAuthError extends Error {
  readonly status: number;
  readonly code: string;
  readonly requestId: string;
  readonly retryable: boolean;
  toJSON(): object;
}

Specialized Errors

ClassStatusCodeRetryableWhen you will see it
AuthenticationError401authentication_requiredNoMissing or invalid credentials
ForbiddenError403forbiddenNoValid credentials, insufficient permissions
NotFoundError404not_foundNoResource does not exist
ConflictError409conflictNoDuplicate resource
ValidationError422validation_errorNoInput validation failed (includes errors: FieldError[])
RateLimitError429rate_limit_exceededYesRate limit exceeded (includes retryAfter: number)
InternalError500internal_errorYesServer-side error

Usage

typescript
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:

typescript
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