Skip to content

UnlikeOtherAI/UnlikeOtherAuthenticator

Repository files navigation

Unlike Other Authenticator

A centralized OAuth & authentication service designed to provide unified login across multiple products with configurable branding, UI, and security features.

What It Does

Unlike Other Authenticator is a stateless, API-first authentication service that enables:

  • Unified authentication across 4–5+ products with a single account per email
  • Multiple auth methods: Email/password, Google, Apple, Facebook, GitHub, LinkedIn
  • Configurable branding: Per-client UI theming, logos, colors, and language support
  • Optional 2FA: TOTP-based two-factor authentication
  • Secure configuration: Tamper-proof JWT-based config delivery
  • Zero admin UI: Client onboarding through signed configuration only

How It Works

Trust Model

  1. Client Identification: Each client is identified by a verified domain. The hash of (domain + shared secret) becomes the client ID.
  2. Config Delivery: All client configuration is delivered as a signed JWT. The OAuth server verifies the JWT signature before trusting any config.
  3. OAuth Flow: Uses the standard authorization code flow. Client popup redirects with a code, which the client backend exchanges for an access token.
  4. Token Pair: Access tokens remain short-lived JWTs (15–60 minutes). Client backends also receive rotating refresh tokens for server-side session renewal.

Core Principles

  • Email is the canonical user identifier
  • No email enumeration protection across all flows
  • All client config is signed and verified
  • Everything UI-related is templated and config-driven
  • No avatars stored locally (external URLs only)
  • Generic error messages only (no information leakage)

Configuration Options

Configuration is delivered as a signed JWT with the following properties:

Required Fields

  • domain — Client domain (e.g., app.example.com)
  • redirect_urls — Array of allowed OAuth redirect URLs
  • enabled_auth_methods — Array of enabled methods: ["email", "google", "apple", "facebook", "github", "linkedin"]
  • ui_theme — Complete theme object (colors, radii, typography, logo URL, density)
  • language_config — Single language string or array of language codes

Optional Fields

  • 2fa_enabled — Boolean to enable/disable 2FA (default: false)
  • debug_enabled — Boolean to enable debug endpoints (default: false)
  • allowed_social_providers — Array of social provider names to enable
  • user_scope"global" (default) or "per_domain" (isolate users per domain)
  • language — Selected language (must be in language_config if provided)
  • org_features — Organisation/team/group feature configuration (see below)

Organisation Features (Optional)

Enable organisations, teams, and groups by adding org_features to the config:

{ "org_features": { "enabled": true, "groups_enabled": false, "max_teams_per_org": 100, "max_members_per_org": 1000, "max_team_memberships_per_user": 50, "org_roles": ["owner", "admin", "member"] } }

When enabled, the access token JWT includes an org claim with the user's organisation, team, and group memberships. Groups are managed exclusively through the Internal API (/internal/org/) using signed requests. See Section 24 of the brief for the full specification.

JWT Signing

All config JWTs must be signed with the shared secret using HS256. Expected claims:

  • aud — Auth service identifier (set in AUTH_SERVICE_IDENTIFIER env var)
  • exp — Optional expiration (configs are verified on every request)

Installation

Prerequisites

  • Node.js 18+ and npm
  • PostgreSQL 14+
  • Social provider OAuth credentials (Google, Apple, Facebook, GitHub, LinkedIn)
  • SMTP server (optional, for email functionality)

1. Clone the Repository

git clone https://github.com/yourusername/unlike-other-authenticator.git cd unlike-other-authenticator

2. Install Dependencies

npm install

This installs dependencies for both the API and Auth workspaces.

3. Environment Variables

Create .env files in both /API and /Auth directories.

API Environment Variables (/API/.env)

# Required SHARED_SECRET=your-secret-key-here AUTH_SERVICE_IDENTIFIER=auth.yourservice.com DATABASE_URL=postgresql://user:password@localhost:5432/auth_db # Social OAuth Providers GOOGLE_CLIENT_ID=your-google-client-id GOOGLE_CLIENT_SECRET=your-google-client-secret # ... (Apple, Facebook, GitHub, LinkedIn credentials) # Email Service (optional) EMAIL_PROVIDER=smtp # or 'disabled' EMAIL_FROM=noreply@yourservice.com EMAIL_REPLY_TO=support@yourservice.com SMTP_HOST=smtp.yourprovider.com SMTP_PORT=587 SMTP_SECURE=false SMTP_USER=your-smtp-username SMTP_PASSWORD=your-smtp-password # Optional Configuration ACCESS_TOKEN_TTL=30 # minutes (15-60) LOG_RETENTION_DAYS=90

4. Database Setup

# Generate Prisma client npm run prisma:generate --workspace API # Run database migrations npm run prisma:migrate:dev --workspace API

5. Run Development Servers

# Terminal 1: Run API server npm run dev:api # Terminal 2: Run Auth UI npm run dev:auth

The API runs on http://localhost:3000 by default. The Auth UI runs on http://localhost:5173 by default.

6. Build for Production

npm run build

This builds both API and Auth workspaces.

7. Run in Production

# Apply production migrations npm run prisma:migrate:deploy --workspace API # Start API server npm run start --workspace API

Architecture

Project Structure

/API — Node.js OAuth/auth server (Fastify) /Auth — React auth UI (Vite + Tailwind) /Docs — Full specification and architecture docs brief.md — Complete product specification techstack.md — Technology stack and structure Auth/ architecture-api.md — API layered architecture architecture-auth.md — Auth UI component architecture Admin/ — Admin panel documentation /Admin — Admin panel UI (React CSR) CLAUDE.md — Agent/contributor instructions 

API Architecture

The API follows a layered architecture:

Request → Routes → Middleware → Services → Database (Prisma) 
  • Routes (/src/routes): Thin handlers that validate input and call services
  • Middleware (/src/middleware): Config verification, domain auth, error handling
  • Services (/src/services): Business logic for auth, users, tokens, social providers, email
  • Utils (/src/utils): Pure helper functions (hashing, validation, errors)

Key Rules:

  • No code file longer than 500 lines
  • Thin routes, fat services
  • All errors are generic to users, detailed in internal logs
  • Prisma for all database access

Auth UI Architecture

The Auth UI is a React application with config-driven theming:

PopupContainer └── ThemeProvider (config-driven) └── I18nProvider (language-driven) └── AuthLayout └── [Page Components] 
  • Components (/src/components): Reusable UI primitives (buttons, cards, inputs) and auth forms
  • Pages (/src/pages): Auth flow screens (login, register, 2FA setup, etc.)
  • Theme (/src/theme): Maps config theme to Tailwind classes
  • i18n (/src/i18n): Translation loading with AI fallback for missing keys

Key Rules:

  • Tailwind-only styling (no other CSS frameworks)
  • All theming from config (no hardcoded brand styles)
  • One component per file for reusable components
  • No component file longer than 500 lines

Database Schema

Core Tables:

  • users — User accounts (email, password hash, name, avatar URL, 2FA settings)
  • domain_roles — Per-domain role assignments (superuser vs user)
  • login_logs — Audit trail of authentication events
  • verification_tokens — One-time tokens for email verification and password reset

Organisation Tables (opt-in via org_features config):

  • organisations — Tenant organisations, scoped per domain
  • org_members — User-to-org membership with configurable roles
  • teams — Named groups of users within an organisation
  • team_members — User-to-team membership with lead/member roles
  • groups — Named collections of teams (enterprise feature)
  • group_members — User-to-group membership with admin flag

User Scope:

  • Global (default): One email = one user across all domains
  • Per-domain: Same email on different domains = separate user records

Security Model

  • No email enumeration: All responses are generic ("Check your email")
  • Shared secret: Single global secret (never exposed, env var only)
  • Config integrity: All configs signed with JWT, verified on every request
  • Domain verification: Runs on each auth initiation (not cached)
  • Social email trust: Only provider-verified emails accepted
  • Short-lived access tokens: Access tokens expire in 15–60 minutes and are renewed through rotating refresh tokens
  • Generic errors: All user-facing error messages are non-specific

Development

Available Scripts

# Development npm run dev:api # Start API server in watch mode npm run dev:auth # Start Auth UI dev server # Building npm run build # Build both workspaces # Code Quality npm run lint # Lint all workspaces npm run format # Format code with Prettier npm run typecheck # TypeScript type checking # Testing npm run test # Run tests in all workspaces # Database npm run prisma:generate --workspace API # Generate Prisma client npm run prisma:migrate:dev --workspace API # Create and apply migration npm run prisma:studio --workspace API # Open Prisma Studio

Testing

# Run all tests npm test # Run API tests only npm test --workspace API # Watch mode (during development) npm test -- --watch --workspace API

Tests are written using Vitest and cover:

  • Unit tests for all services
  • Integration tests for API endpoints
  • Security tests (enumeration protection, generic errors, token validation)

Client Integration

1. Generate Signed Config

On your client backend:

import jwt from 'jsonwebtoken'; const config = { domain: 'app.example.com', redirect_urls: ['https://app.example.com/auth/callback'], enabled_auth_methods: ['email', 'google'], ui_theme: { colors: { primary: '#3b82f6', secondary: '#64748b' }, borderRadius: '0.5rem', // ... full theme config }, language_config: ['en', 'es'], 2fa_enabled: true, user_scope: 'global' }; const configJWT = jwt.sign(config, process.env.SHARED_SECRET, { audience: 'auth.yourservice.com', algorithm: 'HS256' }); // Serve this JWT at a URL accessible to the auth server

2. Initiate OAuth Flow

On your client frontend:

const configUrl = 'https://app.example.com/api/auth-config'; // serves the JWT const authUrl = `https://auth.yourservice.com/oauth/authorize?config_url=${encodeURIComponent(configUrl)}`; window.open(authUrl, 'oauth', 'width=500,height=700');

3. Handle OAuth Callback

On your client backend:

// Callback route receives the authorization code app.get('/auth/callback', async (req, res) => { const { code } = req.query; // Exchange code for an access token + refresh token pair const response = await fetch('https://auth.yourservice.com/auth/token', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${hashDomainAndSecret(domain, sharedSecret)}` }, body: JSON.stringify({ code }) }); const { access_token, refresh_token } = await response.json(); // Verify and decode the JWT access token const user = jwt.verify(access_token, process.env.SHARED_SECRET); // Store the refresh token server-side only (for example an HttpOnly cookie) res.cookie('refresh_token', refresh_token, { httpOnly: true, secure: true, sameSite: 'lax' }); // Set session and redirect req.session.userId = user.id; res.redirect('/dashboard'); });

API Endpoints

Authentication

  • POST /auth/login — Email/password login
  • POST /auth/register — Email registration (sends verification email)
  • POST /auth/verify-email — Verify email with token
  • POST /auth/reset-password — Request password reset
  • GET /auth/callback/:provider — Social OAuth callback
  • POST /auth/token — Exchange authorization code or refresh token for a token pair
  • POST /auth/revoke — Revoke the refresh-token family for logout

Two-Factor Authentication

  • POST /2fa/setup — Initiate 2FA enrollment (returns QR code)
  • POST /2fa/verify — Verify TOTP code during setup or login
  • POST /2fa/reset — Email-based 2FA reset

Domain-Scoped APIs

  • GET /domain/users — List users for domain (requires domain hash token)
  • GET /domain/logs — Get login logs for domain
  • GET /domain/debug — Debug endpoints (superuser only)

Organisations, Teams & Groups (opt-in)

These endpoints require org_features.enabled: true in the config JWT.

User-Facing (require domain hash token + user access token):

  • POST /org/organisations — Create an organisation (auto-creates default team)
  • GET /org/organisations/:orgId — Get organisation details
  • PUT /org/organisations/:orgId — Update organisation
  • DELETE /org/organisations/:orgId — Delete organisation (owner only)
  • GET /org/organisations/:orgId/members — List members
  • POST /org/organisations/:orgId/members — Add member (by userId)
  • POST /org/organisations/:orgId/transfer-ownership — Transfer ownership
  • GET /org/organisations/:orgId/teams — List teams
  • POST /org/organisations/:orgId/teams — Create team
  • GET /org/organisations/:orgId/groups — List groups (read-only)
  • GET /org/me — Current user's org context

Internal API (require domain hash token only, no user token):

  • POST /internal/org/organisations/:orgId/groups — Create group
  • PUT /internal/org/organisations/:orgId/groups/:groupId — Update group
  • DELETE /internal/org/organisations/:orgId/groups/:groupId — Delete group
  • POST /internal/org/organisations/:orgId/groups/:groupId/members — Add group member
  • PUT /internal/org/organisations/:orgId/groups/:groupId/members/:userId — Toggle is_admin
  • DELETE /internal/org/organisations/:orgId/groups/:groupId/members/:userId — Remove group member
  • PUT /internal/org/organisations/:orgId/teams/:teamId/group — Assign/unassign team to group

See Section 24 of the brief for the full specification.

Health

  • GET /health — Health check endpoint

Contributing

See CLAUDE.md for contributor guidelines and Docs/brief.md for the complete specification.

Key Rules:

  • Read the brief before making changes
  • Never remove content from Docs/brief.md unless explicitly instructed
  • Follow architecture patterns in Docs/Auth/architecture-api.md and Docs/Auth/architecture-auth.md
  • No code file longer than 500 lines
  • Keep the codebase reusable and clean
  • Security first: no enumeration, no information leakage, all config verified

License

MIT — See LICENSE file for details.

Support

For issues and questions:


Built with: Node.js, TypeScript, Fastify, React, Tailwind CSS, PostgreSQL, Prisma

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages