Passwordless authentication you can own. Bang, you're in!
Email β access code β MFA β JWT. No passwords. No Cognito. No Auth0. No vendor lock-in. A single Hono service you run yourself.
The access code IS the password β and the password expires every month, by construction.
Most auth systems store password hashes and accept whatever the user typed as long as it matches. BangAuth doesn't store a password hash for the user at all. Instead, the per-user access code is SHA-256(email + "YYYY-MM" + secret). On every login, BangAuth re-derives the expected code from the current month and compares. That has three concrete consequences:
- No password database to breach. There's nothing to dump. Even a full server compromise leaks the signing key, not user passwords.
- Stolen codes self-expire at the month boundary β with a 3-day grace period across the calendar flip so users aren't locked out at midnight on the 1st.
- The same property holds for JWT signing keys. They rotate monthly on the same schedule. A leaked key compromises β€ 1 month of issued tokens, not the system's history.
This is the design AWS Cognito and Auth0 could have shipped and chose not to, because vendor revenue depends on stickiness. BangAuth chose simplicity instead.
1. User enters email β POST /auth/login
2. Access code emailed β SHA-256(email + monthly salt)
3. User clicks link or β POST /auth/verify
enters code
4. MFA (if enabled) β POST /auth/mfa/verify (TOTP)
5. JWT issued β authenticated β
Codes rotate monthly by design β no password database to breach, no tokens to steal. The code IS the password, and it expires every month.
BangAuth is a standalone Hono service you run as a sidecar to your app and call over HTTP. The MVP ships as a container:
# Clone and run with sensible defaults (console email adapter, in-memory key store)
git clone https://github.com/wfredricks/bangauth
cd bangauth
npm install
BANGAUTH_APP_NAME="My App" \
BANGAUTH_ALLOWED_DOMAINS="mycompany.com,partner.org" \
npm start
# BangAuth is now listening on :3000# Or build the image and run as a sidecar in your compose stack
docker build -t bangauth .
docker run -p 3000:3000 \
-e BANGAUTH_ALLOWED_DOMAINS="mycompany.com" \
bangauth// From your application code, call BangAuth's endpoints:
await fetch('http://bangauth:3000/auth/login', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ email: 'alice@mycompany.com' }),
});
// User receives an email with a magic link.
// On callback, POST to /auth/verify, then /auth/mfa/verify if MFA is enrolled.
// You get a signed JWT; mount your own middleware to verify it.About "embed it like middleware." The README's earlier drafts promised an in-process Hono middleware import (
import { createBangAuth } from 'bangauth'). That's the right shape for a future v0.2 release β the engine is pure functions already β but the v0.1 line ships and is tested as a standalone service. Use it over HTTP for now.
Authentication
- π§ Email-based login β no passwords, ever
- π Monthly code rotation β SHA-256 with YYYY-MM salt
- π MFA support β TOTP (Google Authenticator, Authy)
- π« JWT tokens β signed with rotating HMAC keys
- π‘οΈ Brute-force protection β max 5 MFA attempts per session
- π Recovery codes β 10 single-use backup codes (XXXX-XXXX, ambiguous chars removed)
Adapters (plug in your infrastructure)
- Email: SES | SMTP | SendGrid | Console (dev)
- Key storage: AWS Secrets Manager | Environment variables | File | Memory (testing)
- Config: AWS SSM | YAML file | Environment variables
Security
- NIST 800-53 aligned β AC-2, AC-3, IA-2, IA-5, SC-13
- Key rotation β monthly, automated via cron/EventBridge
- Audit trail β every auth event emitted to event bus
- No password storage β nothing to breach
- Domain allowlist β only approved organizations
# bangauth.yaml
app:
id: my-app
name: My Application
loginUrl: https://myapp.com/login
auth:
allowedDomains:
- mycompany.com
- partner.org
codeRotation: monthly
tokenTTL: 86400 # 24 hours
mfa:
policy: optional # required | optional | off
issuer: My Application # Shown in authenticator app
maxAttempts: 5
email:
provider: ses # ses | smtp | sendgrid | console
fromAddress: auth@myapp.com
fromName: My App Auth
keys:
provider: secrets-manager # secrets-manager | env | file | memory
secretPath: /myapp/auth/keys
algorithm: HS256| BangAuth | Auth0 | Cognito | Roll Your Own | |
|---|---|---|---|---|
| Vendor lock-in | None | High | AWS-only | None |
| Passwords | None (email codes) | Yes | Yes | Probably |
| MFA | Built-in | Add-on | Config hell | You build it |
| Self-hosted | Yes | No | AWS-only | Yes |
| FedRAMP-ready | You own it | Their ATO | Their ATO | Your problem |
| Cost | Free + infra | $$$$/month | Usage-based | Engineering time |
| Time to integrate | 5 minutes | Hours | Days | Weeks |
POST /auth/login
{ "email": "alice@mycompany.com" }
β Check domain allowlist
β Generate code: SHA-256(email + YYYYMM + secret)
β Send email with link: loginUrl?token=<code>
β Response: { "message": "Check your email" }
POST /auth/verify
{ "token": "<code from email>" }
β Recompute: SHA-256(email + YYYYMM + secret)
β Compare with submitted code
β If MFA enrolled: return mfaSessionToken
β If no MFA: issue JWT immediately
POST /auth/mfa/verify
{ "mfaSessionToken": "<from step 2>", "code": "123456" }
β Verify TOTP code against enrolled secret
β Check attempt count (max 5)
β Issue JWT
GET /api/anything
Authorization: Bearer <jwt>
β Verify JWT signature
β Check expiry
β Extract user identity
β Proceed β
npm test43 tests covering:
- Token engine (generation, verification, rotation)
- Domain allowlist
- Recovery codes
- MFA sessions (brute-force protection)
- TOTP (enrollment, QR generation)
- Email templates
All tests run with in-memory adapters β no AWS, no network, instant.
The Dockerfile at the repo root builds a self-contained image that runs the Hono server on port 3000. This is how BangAuth ships and how it's exercised in the Twin Constellation today.
docker build -t bangauth .
docker run -p 3000:3000 -e BANGAUTH_ALLOWED_DOMAINS="..." bangauthEnvironment variables BangAuth recognises:
| Variable | Default | Purpose |
|---|---|---|
BANGAUTH_APP_NAME |
BangAuth |
Shown in emails and the login page |
BANGAUTH_APP_ID |
credence-twin-standard |
Token audience identifier |
BANGAUTH_ALLOWED_DOMAINS |
* |
Comma-separated allowlist (e.g. *.mil,gmail.com) |
BANGAUTH_MFA_POLICY |
optional |
required / optional / off |
BANGAUTH_MFA_ISSUER |
BangAuth |
TOTP issuer label shown in authenticator apps |
BANGAUTH_PORT |
3000 |
HTTP listen port |
BANGAUTH_NATS_URL |
(unset) | If set, audit events publish to NATS |
The cdk/ directory holds an in-progress CDK stack that deploys BangAuth as Lambda functions behind API Gateway with SES and Secrets Manager. It is not the recommended path today β use the Docker image until the CDK stack catches up to the latest server surface.
βββββββββββββββββββββββββββββββββββββββββββββββ
β Your Application β
β calls BangAuth over HTTP / fetch β
ββββββββββββββββββββββββββ¬ββββββββββββββββββββββ
β HTTP (JSON)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββ
β BangAuth Server β
β Hono routes Β· Login Β· Verify Β· MFA Β· JWT β
β β Pure functions (token, totp, recovery) β
βββββββββββββββββββββββββββββββββββββββββββββββ€
β Adapter Layer β
β Email β Keys β Users β
β SES/SMTP β SM/Env β Memory/Persistent β
β Console β Memory β β
βββββββββββββββββββββββββββββββββββββββββββββββββ
Pure auth logic on top of pluggable adapters. The core engine (token.ts, totp.ts, recovery.ts, domain.ts, mfa-session.ts) is pure functions, no I/O. The Hono server is the I/O shell. The adapters bridge to your infrastructure choices. That separation is what makes the v0.2 "embed as library" path tractable β the engine is already library-shaped; only the server wrapper needs replacing with an in-process middleware.
- PolyGraph β own your database
- PolyGraph Viz β see your data
- BangAuth β own your identity
Apache 2.0 β use it, modify it, own it.
No passwords. No vendor lock-in. No excuses. Bang, you're in! π₯