Skip to main content
AuthenlySign
Resources/Developer Guide

AuthenlySign Developer Guide

Version: 2.0 Last Updated: February 2026 Target Audience: Developers, Integration Engineers, Technical Architects


Table of Contents

  1. Architecture Overview
  2. Technology Stack
  3. Local Development Setup
  4. Directory Structure
  5. Database Schema
  6. API Reference
  7. Authentication & Authorization
  8. Webhooks
  9. Security Architecture
  10. Testing
  11. Deployment
  12. Contributing

Architecture Overview

+---------------------------------------------------+
|                    Frontend                        |
|   Next.js 16 App Router + React 19.2 RSC          |
+------------------------+--------------------------+
                         |
+------------------------+--------------------------+
|              Proxy (Edge Middleware)               |
|   WAF -> Rate Limiter -> Supabase Session Refresh |
+--------+----------+-----------+-------------------+
         |          |           |
  +------+---+ +---+-----+ +--+------------+
  | Supabase | | Stripe  | | Vercel Blob   |
  | Postgres | | Payments| | File Storage  |
  | + Auth   | |         | |               |
  +----------+ +---------+ +---------------+
         |
  +------+--------+
  | Upstash Redis |
  | Rate Limiting |
  | + Caching     |
  +---------------+

Data Flow: Document Signing

  1. User uploads PDF via /dashboard/documents
  2. File stored in Vercel Blob; document record created in PostgreSQL
  3. Sender adds fields and signers, then sends
  4. Each signer receives an email with a secure link to /sign/[documentId]
  5. Signer completes fields, submits
  6. POST /api/documents/[documentId]/sign validates input, generates PKI digital signature, creates SHA-256 hash, stamps with trusted timestamp authority
  7. Compliance logger records the event with hash-chain integrity
  8. Completion notifications sent; signed PDF generated

Technology Stack

LayerTechnology
FrameworkNext.js 16 (App Router, Turbopack)
UIReact 19.2, TypeScript 5.x, Tailwind CSS v4, shadcn/ui
DatabaseSupabase PostgreSQL (60 tables, full RLS)
AuthSupabase Auth (email/password, SAML SSO, 2FA)
File StorageVercel Blob
PaymentsStripe (Checkout, Customer Portal, Webhooks)
EmailResend (transactional email)
Cache/Rate LimitUpstash Redis (lazy-loaded, dynamic import)
AIVercel AI Gateway (fraud detection, document analysis)
HostingVercel (serverless functions + Edge)

Local Development Setup

Prerequisites

  • Node.js 18+ with npm, pnpm, or yarn
  • A Supabase project (for database and auth)
  • A Stripe account (for payment features)
  • Git

Step 1: Clone and Install

bash
git clone https://github.com/your-org/authenlysign.git
cd authenlysign
pnpm install

Step 2: Environment Variables

Create .env.local with:

env
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key

# App
NEXT_PUBLIC_SITE_URL=http://localhost:3000

# Stripe
STRIPE_SECRET_KEY=sk_test_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...

# Vercel Blob
BLOB_READ_WRITE_TOKEN=vercel_blob_...

# Email
RESEND_API_KEY=re_...

# Security
CRON_SECRET=random_secret_for_cron_jobs

Step 3: Database Migrations

Run the SQL scripts in the /scripts directory in numerical order against your Supabase SQL Editor. There are 82+ migration files covering all 60 tables, RLS policies, triggers, and indexes.

Step 4: Start Development

bash
pnpm dev

Visit http://localhost:3000. Create a test account, upload a PDF, and complete the full signing flow to verify setup.


Directory Structure

authenlysign/
+-- app/                          # Next.js App Router (104 pages)
|   +-- api/                     # API route handlers (159 routes)
|   |   +-- admin/               # Admin-only endpoints
|   |   +-- auth/                # Authentication endpoints
|   |   +-- documents/           # Document CRUD + signing
|   |   +-- cron/                # Scheduled jobs
|   |   +-- health/              # Health check endpoints
|   |   +-- security/            # Security monitoring
|   |   +-- webhooks/            # Webhook handlers
|   +-- auth/                    # Auth pages (signin, signup, verify, etc.)
|   +-- dashboard/               # Protected dashboard pages
|   |   +-- admin/               # Admin panels
|   |   +-- documents/           # Document management
|   |   +-- templates/           # Template management
|   |   +-- settings/            # User and org settings
|   +-- sign/[documentId]/       # Public signing ceremony
|   +-- layout.tsx               # Root layout with fonts + metadata
|   +-- page.tsx                 # Landing page
+-- components/                   # React components (120 files)
|   +-- ui/                      # shadcn/ui primitives
+-- lib/                          # Server utilities (106 modules)
|   +-- supabase/                # Supabase client/server/proxy/admin
|   +-- api-auth.ts              # requireAuth / requireAdmin guards
|   +-- input-sanitizer.ts       # SQL/XSS/CMD/Path injection detection
|   +-- compliance-logger.ts     # Hash-chained audit trail
|   +-- waf.tsx                  # Web Application Firewall engine
|   +-- pki-manager.ts           # PKI digital signatures
|   +-- webhook*.ts              # Webhook delivery and retry
+-- hooks/                        # React hooks (use-auth, use-mobile, etc.)
+-- scripts/                      # SQL migrations (82+ files)
+-- docs/                         # Additional documentation
+-- proxy.ts                      # Edge middleware (WAF + rate limit + auth)
+-- next.config.mjs               # Next.js config with security headers

Database Schema

The application uses 60 PostgreSQL tables managed via Supabase. All tables have Row Level Security (RLS) enabled with appropriate policies.

Core Tables

TablePurpose
documentsDocument records (title, status, file URL, expiration)
document_signersSigner assignments per document
document_field_dataField values captured during signing
document_fieldsField definitions (type, position, page)
document_templatesReusable document templates
user_profilesExtended user data (name, company, role, plan)
organizationsOrganization/team records
audit_logsImmutable audit trail with hash chain
signing_sessionsActive signing session tracking
signer_authenticationSigner identity verification records

Key Relationships

auth.users (Supabase Auth)
  |-- user_profiles (1:1, auto-created via trigger)
  |-- documents (1:many)
  |     |-- document_signers (1:many)
  |     |-- document_fields (1:many)
  |     |-- document_field_data (1:many)
  |     |-- audit_logs (1:many)
  |-- organizations (many:many via org_members)

RLS Pattern

All tables follow the ownership pattern:

sql
-- Users can only access their own data
CREATE POLICY "users_own_data" ON documents
  FOR ALL USING (auth.uid() = user_id);

-- Signers can access documents they are assigned to
CREATE POLICY "signers_access" ON documents
  FOR SELECT USING (
    id IN (SELECT document_id FROM document_signers WHERE signer_email = auth.email())
  );

API Reference

Authentication

All API requests require one of:

  1. Session Cookie (browser): Automatically managed by Supabase Auth
  2. API Key (programmatic): Authorization: Bearer sk_live_...
  3. Service Role (server-to-server): Authorization: Bearer <service_role_key>

Core Endpoints

Documents

MethodEndpointDescription
POST/api/documents/uploadUpload a new PDF
GET/api/documents/[id]Get document details
POST/api/documents/[id]/send-for-signingSend to signers
POST/api/documents/[id]/signSubmit signature
GET/api/documents/[id]/statusCheck signing status
POST/api/documents/[id]/remindSend reminder
GET/api/documents/[id]/downloadDownload signed PDF
POST/api/documents/[id]/verifyVerify signature chain

Templates

MethodEndpointDescription
POST/api/templates/createCreate template
GET/api/templates/[id]Get template
PUT/api/templates/[id]/updateUpdate template

Users & Auth

MethodEndpointDescription
GET/api/user/profileGet current user profile
PUT/api/user/profileUpdate profile
POST/api/auth/2fa/setupInitialize 2FA setup
POST/api/auth/2fa/verifyVerify 2FA code

Subscriptions

MethodEndpointDescription
POST/api/subscription/createCreate subscription
POST/api/subscription/changeChange plan
POST/api/subscription/cancelCancel subscription
POST/api/subscription/portalStripe Customer Portal

Admin (requires admin role)

MethodEndpointDescription
GET/api/admin/statsOrganization statistics
GET/api/admin/usersList all users
POST/api/admin/users/createCreate user
POST/api/admin/users/bulk-importBulk import users
GET/api/admin/security/eventsSecurity event log

Health Checks

MethodEndpointDescription
GET/api/healthOverall health
GET/api/health/databaseDatabase connectivity
GET/api/health/securitySecurity posture (admin)
GET/api/health/productionFull production check

Error Format

json
{
  "error": "DOCUMENT_NOT_FOUND",
  "message": "Document not found or access denied",
  "details": { "documentId": "..." }
}

Common codes: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_ERROR, RATE_LIMIT_EXCEEDED, INTERNAL_ERROR.

Rate Limiting

  • Default: 1000 requests/hour per API key, 100/hour per IP (unauthenticated)
  • Headers: X-RateLimit-Remaining, X-RateLimit-Reset
  • 429 response includes Retry-After header

Authentication & Authorization

Supabase Auth Clients

typescript
// Browser client (singleton)
import { createClient } from "@/lib/supabase/client"
const supabase = createClient()

// Server client (per-request)
import { createServerSupabaseClient } from "@/lib/supabase/server"
const supabase = await createServerSupabaseClient()

// Service role client (admin operations, bypasses RLS)
import { createServiceRoleClient } from "@/lib/supabase/server"
const supabase = createServiceRoleClient()

API Route Guards

typescript
import { requireAuth, requireAdmin, isAuthError } from "@/lib/api-auth"

// Require authenticated user
export async function GET() {
  const auth = await requireAuth()
  if (isAuthError(auth)) return auth
  // auth.user and auth.supabase are available
}

// Require admin role
export async function POST() {
  const auth = await requireAdmin()
  if (isAuthError(auth)) return auth
  // Only admins reach here
}

Dashboard Protection

The dashboard layout (app/dashboard/layout.tsx) is an async Server Component that:

  1. Calls supabase.auth.getUser() on every request
  2. Redirects to /auth/signin if no user is found
  3. Fetches the user profile for display (name, role, admin badge)

The proxy middleware (proxy.ts) also refreshes Supabase sessions and redirects unauthenticated users away from /dashboard/*.


Webhooks

Event Types

EventTrigger
document.createdNew document uploaded
document.sentDocument sent for signing
document.signedOne signer completed
document.completedAll signers completed
document.expiredDocument past expiration
document.voidedDocument cancelled
user.createdNew user signed up
subscription.changedPlan upgraded/downgraded

Payload Structure

json
{
  "id": "evt_abc123",
  "type": "document.completed",
  "created": 1709251200,
  "data": {
    "id": "doc_abc123",
    "title": "NDA Agreement",
    "status": "signed",
    "completedAt": "2026-02-19T12:00:00Z"
  }
}

Signature Verification

typescript
import crypto from "crypto"

function verifyWebhook(payload: string, signature: string, secret: string): boolean {
  const expected = crypto.createHmac("sha256", secret).update(payload).digest("hex")
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(\`sha256=\${expected}\`))
}

Retry Policy

Failed deliveries retry with exponential backoff: immediately, 5 min, 30 min, 2 hours, 6 hours. After 5 failures, the delivery moves to the dead-letter queue (/api/webhooks/dead-letter-queue).


Security Architecture

Proxy Middleware (proxy.ts)

All requests pass through the Edge proxy which runs three checks in order:

  1. WAF Inspection: analyzeRequest() from lib/waf.tsx scans URL, method, and headers for injection patterns. Blocked requests get HTTP 403.
  2. Rate Limiting: checkRateLimit() from lib/proxy-rate-limit.ts enforces per-IP limits using in-memory counters (Redis optional).
  3. Session Refresh: updateSession() from lib/supabase/proxy.ts refreshes the Supabase JWT and redirects unauthenticated users from protected routes.

Input Sanitization (lib/input-sanitizer.ts)

Available for use in any API route:

typescript
import { validateInput, validateRequestBody } from "@/lib/input-sanitizer"

const result = validateInput(userInput)
if (!result.safe) {
  // result.threats contains ["sql_injection", "xss", etc.]
}

const bodyResult = validateRequestBody(requestBody)  // Recursive check of all values

Security Headers (next.config.mjs)

Production headers include: HSTS (1 year, includeSubDomains), CSP (script-src, style-src, connect-src, frame-ancestors), X-Frame-Options DENY, X-Content-Type-Options nosniff, Referrer-Policy strict-origin-when-cross-origin, Permissions-Policy (camera, microphone, geolocation disabled), X-Download-Options noopen, X-Permitted-Cross-Domain-Policies none, upgrade-insecure-requests.

Compliance Logging (lib/compliance-logger.ts)

All security and compliance events are logged with SHA-256 hash chaining:

typescript
import { logComplianceEvent } from "@/lib/compliance-logger"

await logComplianceEvent({
  documentId: "...",
  action: "document_signed",
  eventType: "document_signed",
  actorEmail: "signer@example.com",
  actorIpAddress: ip,
  details: { certificate_id: "...", document_hash: "..." },
})

Testing

Health Check Endpoints

Use the built-in health endpoints for integration testing:

bash
curl https://your-app.vercel.app/api/health
curl https://your-app.vercel.app/api/health/database
curl https://your-app.vercel.app/api/health/security  # Requires admin auth

E2E Testing

The admin E2E testing panel at /dashboard/admin/e2e-testing provides automated test suites for:

  • Authentication flows (signup, signin, 2FA, password reset)
  • Document lifecycle (upload, send, sign, verify, download)
  • Email delivery (invitation, reminder, completion)
  • User management (create, update, delete, bulk import)

Security Test Suite

The /api/health/security endpoint runs 11 automated checks including:

  • WAF engine validation (6 attack vectors)
  • Injection defense testing (25+ payloads: SQL, XSS, CMD, path traversal)
  • Parameterized query verification
  • RLS policy coverage
  • Encryption standards
  • Audit trail integrity

Deployment

Vercel Deployment

  1. Connect your GitHub repository to Vercel
  2. Environment variables are injected automatically from integrations (Supabase, Stripe, Blob, Upstash)
  3. Push to main branch triggers production deployment
  4. Preview deployments created for pull requests

Post-Deployment Verification

bash
# Check overall health
curl https://your-app.vercel.app/api/health

# Check production readiness
curl https://your-app.vercel.app/api/health/production

# Check security posture (requires admin cookie)
curl -H "Cookie: ..." https://your-app.vercel.app/api/health/security

Environment Variables

VariableRequiredDescription
NEXT_PUBLIC_SUPABASE_URLYesSupabase project URL
NEXT_PUBLIC_SUPABASE_ANON_KEYYesSupabase anonymous key
SUPABASE_SERVICE_ROLE_KEYYesSupabase service role key
STRIPE_SECRET_KEYYesStripe secret key
BLOB_READ_WRITE_TOKENYesVercel Blob token
RESEND_API_KEYYesResend email API key
CRON_SECRETYesSecret for cron job auth
KV_REST_API_URLOptionalUpstash Redis URL
KV_REST_API_TOKENOptionalUpstash Redis token

Contributing

Development Workflow

  1. Create a feature branch from main
  2. Make changes following existing code patterns
  3. Ensure all health endpoints pass
  4. Open a pull request with a clear description
  5. Vercel creates a preview deployment automatically
  6. Request code review from a team member
  7. Merge to main after approval

Code Style

  • TypeScript with strict mode
  • Tailwind CSS v4 with design tokens (use bg-background, text-foreground, etc. -- never direct colors)
  • shadcn/ui components as primitives
  • Server Components by default; "use client" only when needed
  • Supabase JS client for all database access (parameterized queries by default)
  • Dynamic imports for heavy/optional modules (e.g., await import("@upstash/redis"))

Commit Conventions

Use conventional commits: feat:, fix:, docs:, refactor:, chore:, security:

Adding a New API Route

  1. Create app/api/your-feature/route.ts
  2. Import and call requireAuth() or requireAdmin() from @/lib/api-auth
  3. Validate input with validateRequestBody() from @/lib/input-sanitizer
  4. Use supabase.from("table") for database access (never raw SQL)
  5. Log compliance events with logComplianceEvent() for sensitive actions
  6. Return structured JSON responses with appropriate HTTP status codes

AuthenlySign Developer Guide v2.0 -- February 2026 For the latest version, visit /resources/docs/developer-guide

AUTH DEBUG