Skip to content

Commit f500e76

Browse files
committed
feat(auth): add neon-backed provider facade and canonical secret migration
1 parent c26dac6 commit f500e76

8 files changed

Lines changed: 410 additions & 111 deletions

File tree

CHARTER.md

Lines changed: 87 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -2,156 +2,141 @@
22

33
## Classification
44
- **Canonical URI**: `chittycanon://core/services/chittyauth-app`
5-
- **Tier**: 3 (Service Layer)
5+
- **Tier**: 1 (Core Identity) — function-based; same tier as `chittyauth`, distinguished by deployment model
66
- **Organization**: CHITTYAPPS
7-
- **Domain**: Custom (not auth.chitty.cc)
7+
- **Domain**: Operator-chosen (no canonical default)
88

99
## Mission
1010

11-
ChittyAuth App is a **standalone authentication and token provisioning service** designed for independent deployment without ChittyOS infrastructure dependencies. Unlike the OS-integrated `chittyauth` service, this app uses Cloudflare-native storage (D1 + KV) and requires no external database connections.
11+
ChittyAuth App is a **standalone authentication and token provisioning service** that delivers ChittyAuth-class token issuance without the ChittyOS shared-database backbone. It is a Cloudflare-native build (D1 + KV) intended for third-party deployments, isolated environments, and per-app token authorities.
1212

1313
## Scope
1414

1515
### IS Responsible For
16-
- User registration and account management
17-
- API token provisioning with HMAC-SHA256 signatures
18-
- Token validation with KV caching (fast path)
19-
- Token refresh and revocation
20-
- Rate limiting via KV namespaces
21-
- Complete audit logging
16+
- User registration and account lifecycle (`/v1/register`)
17+
- API token provisioning, validation, refresh, and revocation
18+
- HMAC-SHA256 signing + SHA-256 hashed-at-rest token storage
19+
- KV-first validation cache (30s TTL) and revocation blocklist
20+
- Per-token rate limiting via KV counters (1h window)
21+
- Append-only audit logging (D1 `audit_logs`)
2222
- OAuth client registration
23-
- D1 SQLite primary storage
24-
- KV-first caching architecture
2523

2624
### IS NOT Responsible For
27-
- ChittyOS ecosystem integration
28-
- Shared identity tables (uses isolated storage)
29-
- Service-to-service tokens (end-user tokens only)
30-
- ChittyID dependency (can work standalone)
25+
- Service-to-service tokens (end-user tokens only — use `chittyauth` for inter-service auth)
26+
- Shared identity tables with `chittyos-core`
27+
- ChittyID minting (this app authenticates, it does not issue ChittyIDs)
28+
- Cross-service identity sharing (storage is isolated by design)
3129

32-
## Comparison to chittyauth
30+
## Comparison to `chittyauth`
3331

34-
| Aspect | chittyauth (CHITTYFOUNDATION) | chittyauth-app (CHITTYAPPS) |
35-
|--------|-------------------------------|---------------------------|
36-
| Database | Neon PostgreSQL (chittyos-core) | D1 + KV |
37-
| Dependencies | ChittyID, ChittyConnect required | Optional integrations |
32+
| Aspect | `chittyauth` (CHITTYFOUNDATION) | `chittyauth-app` (CHITTYAPPS) |
33+
|--------|---------------------------------|------------------------------|
34+
| Database | Neon PostgreSQL (chittyos-core) | D1 (SQLite) + KV |
35+
| Dependencies | ChittyID, ChittyConnect required | Optional (ChittyConnect only) |
3836
| Data Sharing | Shares identity data | Isolated storage |
39-
| Deployment | auth.chitty.cc | Any custom domain |
40-
| Use Case | Core ChittyOS services | Third-party apps |
41-
| Token Type | Service + User tokens | End-user tokens only |
37+
| Domain | `auth.chitty.cc` | Operator-chosen |
38+
| Token Audience | Service + user | End-user only |
39+
| Use Case | Core ChittyOS services | Third-party apps, custom deployments |
4240

4341
## Architecture
4442

45-
### Storage Backend
46-
**D1 Database** (Primary):
47-
- `api_tokens` - Token records and metadata
48-
- `users` - User accounts
49-
- `audit_logs` - Complete audit trail
50-
- `oauth_clients` - OAuth client registrations
51-
52-
**KV Namespaces** (Caching):
53-
- `AUTH_TOKENS` - Token validation cache (30s TTL)
54-
- `AUTH_REVOCATIONS` - Revoked token list
55-
- `AUTH_RATE_LIMITS` - Rate limiting counters (1h window)
56-
- `AUTH_AUDIT` - Audit log buffer
57-
58-
### Token Security
59-
1. Generate token with HMAC-SHA256 signature
60-
2. Hash token with SHA-256
61-
3. Store only hash in D1 (never plaintext)
62-
4. Return plaintext to user (only time visible)
43+
### Storage Bindings
44+
- **D1** (`AUTH_DB`): `users`, `api_tokens`, `audit_logs`, `oauth_clients`
45+
- **KV**:
46+
- `AUTH_TOKENS` — validation cache (30s TTL)
47+
- `AUTH_REVOCATIONS` — revoked-token blocklist
48+
- `AUTH_RATE_LIMITS` — per-token request counters (1h window)
49+
- `AUTH_AUDIT` — audit-log buffer
6350

6451
### Validation Flow
6552
```
66-
Request → Check KV cache (fast path)
67-
→ If miss: Query D1 (slow path)
68-
→ Cache valid token for 30 seconds
69-
→ Return validation result
53+
Request → KV cache (fast path) ──hit──→ return
54+
│ miss
55+
56+
D1 query → cache (30s) → return
7057
```
7158

72-
## API Endpoints
59+
### Token Format
60+
JWT-like `header.payload.signature`:
61+
```json
62+
{
63+
"iss": "chittyauth-app",
64+
"sub": "<user_id>",
65+
"aud": ["<application>"],
66+
"scopes": ["<scope>:<action>"],
67+
"iat": 0, "exp": 0,
68+
"jti": "<unique_token_id>"
69+
}
70+
```
71+
Hashed with SHA-256 before storage; plaintext returned to caller exactly once at issuance.
72+
73+
## API Contract
7374

74-
### Public (No Auth)
75+
### Public (no auth)
7576
| Endpoint | Method | Purpose |
7677
|----------|--------|---------|
77-
| `/v1/register` | POST | Register new user |
78-
| `/health` | GET | Health check |
78+
| `/v1/register` | POST | Register a user and issue first token |
79+
| `/health` | GET | Liveness + binding health |
7980

80-
### Protected (Bearer Token)
81+
### Protected (Bearer token)
8182
| Endpoint | Method | Purpose |
8283
|----------|--------|---------|
83-
| `/v1/tokens/provision` | POST | Provision new API token |
84-
| `/v1/tokens/validate` | POST | Validate token |
85-
| `/v1/tokens/refresh` | POST | Refresh token expiration |
86-
| `/v1/tokens/revoke` | POST | Revoke token |
87-
| `/v1/tokens/stats` | GET | Token usage statistics |
88-
89-
## Token Format
90-
91-
JWT-like structure: `header.payload.signature`
92-
93-
```json
94-
{
95-
"iss": "chittyauth-app",
96-
"sub": "user_id",
97-
"aud": ["myapp"],
98-
"scopes": ["myapp:read", "myapp:write"],
99-
"iat": 1700000000,
100-
"exp": 1700086400,
101-
"jti": "unique_token_id"
102-
}
103-
```
84+
| `/v1/tokens/provision` | POST | Issue a new token |
85+
| `/v1/tokens/validate` | POST | Validate a token |
86+
| `/v1/tokens/refresh` | POST | Refresh expiration |
87+
| `/v1/tokens/revoke` | POST | Revoke immediately |
88+
| `/v1/tokens/stats` | GET | Usage statistics |
10489

10590
## Dependencies
10691

107-
| Type | Service | Purpose |
108-
|------|---------|---------|
109-
| Optional | ChittyConnect | External integration |
110-
| Storage | Cloudflare D1 | SQLite database |
111-
| Storage | Cloudflare KV | Caching and rate limiting |
112-
| Runtime | Cloudflare Workers | Serverless edge |
92+
| Type | Component | Purpose |
93+
|------|-----------|---------|
94+
| Runtime | Cloudflare Workers | Edge serverless host |
95+
| Storage | Cloudflare D1 | Primary persistent store |
96+
| Storage | Cloudflare KV | Cache, rate limit, revocation, audit buffer |
97+
| Optional | ChittyConnect | External identity verification (off by default) |
11398

11499
## Configuration
115100

116101
### Required Secrets
117-
- `TOKEN_SIGNING_KEY` - 256-bit key for HMAC signatures
102+
- `CHITTYAUTH_ISSUED_MINT_API_KEY` — canonical 256-bit HMAC key (rotate quarterly)
118103

119104
### Optional Secrets
120-
- `CHITTYCONNECT_API_KEY` - For ChittyConnect integration
105+
- `CHITTYAUTH_ISSUED_CONNECT_API_KEY` — canonical connect service token if ChittyConnect integration is enabled
106+
- `NEON_OAUTH_CLIENT_ID` / `NEON_OAUTH_CLIENT_SECRET` — when `CHITTYAUTH_PROVIDER=neon`
107+
- Legacy alias support remains for migration: `TOKEN_SIGNING_KEY`, `CHITTYCONNECT_API_KEY`
121108

122109
### Environment Variables
123-
- `ENVIRONMENT` - "development" or "production"
124-
- `DEFAULT_TOKEN_EXPIRY` - Token lifetime (seconds)
125-
- `MAX_TOKENS_PER_USER` - Token limit per user
110+
- `ENVIRONMENT``development` | `production`
111+
- `CHITTYAUTH_PROVIDER``local` | `neon`
112+
- `NEON_OAUTH_HOST` — defaults to `https://oauth2.neon.tech`
113+
- `DEFAULT_TOKEN_EXPIRY` — seconds (default 2592000 = 30d)
114+
- `MAX_TOKENS_PER_USER` — integer cap
126115

127116
## Ownership
128117

129118
| Role | Owner |
130119
|------|-------|
131120
| Service Owner | ChittyApps |
132121
| Technical Lead | @chittyapps-team |
133-
| Contact | auth-app@chitty.cc |
122+
| Security Contact | security@chitty.cc |
123+
| Service Contact | auth-app@chitty.cc |
134124

135125
## Compliance
136126

137-
- [ ] CLAUDE.md development guide present
138-
- [ ] CHARTER.md present
139-
- [ ] CHITTY.md present
140-
- [ ] D1 database initialized with schema
141-
- [ ] All KV namespaces created and bound
142-
- [ ] TOKEN_SIGNING_KEY secret set (256-bit)
143-
- [ ] Health endpoint operational
144-
- [ ] Registration endpoint tested
145-
- [ ] Rate limiting verified
146-
147-
## Security Checklist
148-
149-
- [ ] Rotate TOKEN_SIGNING_KEY quarterly
150-
- [ ] Monitor audit logs for suspicious activity
151-
- [ ] Token expiration set (30 days max)
152-
- [ ] Rate limiting on all endpoints
153-
- [ ] HTTPS only in production
154-
- [ ] Never log token values (only hashes)
127+
Operational gate (must be green before deploy):
128+
- [ ] D1 database created and `schema.sql` applied
129+
- [ ] All four KV namespaces created and bound in `wrangler.toml`
130+
- [ ] `CHITTYAUTH_ISSUED_MINT_API_KEY` set via `wrangler secret put`
131+
- [ ] If Neon-backed mode is enabled: `CHITTYAUTH_PROVIDER=neon` and Neon OAuth secrets are present
132+
- [ ] `/health` returns `{"status":"healthy"}` with `checks.database` and `checks.kv` true
133+
- [ ] `/v1/register` smoke test succeeds end-to-end
134+
- [ ] `/v1/tokens/validate` confirms KV-cache hit on second call
135+
- [ ] CHARTER.md, CHITTY.md, CLAUDE.md, AGENTS.md, SECURITY.md present and consistent
136+
137+
Documentation gate:
138+
- [ ] No mocked/placeholder routes in committed code (per global no-mocks policy)
139+
- [ ] No fake or seeded data in `schema.sql` (real shapes only)
155140

156141
---
157-
*Charter Version: 1.1.0 | Last Updated: 2026-02-21*
142+
*Charter Version: 1.2.0 | Last Updated: 2026-05-02*

MIGRATION_NEON_AUTH.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# ChittyAuth Neon Migration
2+
3+
This repo now supports a provider facade:
4+
- `CHITTYAUTH_PROVIDER=local` (default)
5+
- `CHITTYAUTH_PROVIDER=neon` (Neon OAuth-backed mode)
6+
7+
## Canonical secrets
8+
9+
Use these names for all new deployments:
10+
- `CHITTYAUTH_ISSUED_MINT_API_KEY`
11+
- `CHITTYAUTH_ISSUED_CONNECT_API_KEY`
12+
- `NEON_OAUTH_CLIENT_ID` (Neon mode)
13+
- `NEON_OAUTH_CLIENT_SECRET` (Neon mode)
14+
15+
Legacy aliases still resolve during migration:
16+
- `TOKEN_SIGNING_KEY` -> `CHITTYAUTH_ISSUED_MINT_API_KEY`
17+
- `CHITTYCONNECT_API_KEY` -> `CHITTYAUTH_ISSUED_CONNECT_API_KEY`
18+
19+
## New facade endpoints
20+
21+
- `GET /v1/auth/provider/status`
22+
- `POST /v1/auth/neon/oauth/authorize-url`
23+
- `POST /v1/auth/neon/oauth/token-exchange`
24+
25+
## Cutover sequence
26+
27+
1. Create canonical secrets first (chicken/egg bootstrap).
28+
2. Keep legacy secrets for one deploy window.
29+
3. Deploy with `CHITTYAUTH_PROVIDER=local` and verify token lifecycle still passes.
30+
4. Set `CHITTYAUTH_PROVIDER=neon` and Neon OAuth secrets.
31+
5. Validate Neon OAuth authorize URL and code exchange endpoints.
32+
6. Remove legacy aliases from runtime once all services have migrated.

SECURITY.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Security Policy
2+
3+
## Reporting a Vulnerability
4+
5+
**Do NOT report security vulnerabilities through public GitHub issues.**
6+
7+
### Preferred: GitHub Security Advisories
8+
9+
1. Open https://github.com/CHITTYAPPS/chittyauth-app/security/advisories/new
10+
2. Click **Report a vulnerability**
11+
3. Include: description, reproduction steps, affected versions/commit, observed impact, and any token/credential exposure assessment
12+
13+
### Alternative: Email
14+
15+
**security@chitty.cc**
16+
17+
### Response Timeline
18+
19+
- **Acknowledgement**: 24 hours
20+
- **Triage confirmation**: 48 hours
21+
- **Critical fix**: 7 days
22+
- **High-severity fix**: 14 days
23+
24+
We follow coordinated disclosure and credit reporters unless anonymity is requested.
25+
26+
## Supported Versions
27+
28+
| Version | Supported |
29+
|---------|-----------|
30+
| Production deployment (Cloudflare Workers) | Yes |
31+
| Local `wrangler dev` | Best effort |
32+
| Unmodified forks | Not supported by this team |
33+
34+
## Threat Model & Trust Boundaries
35+
36+
ChittyAuth App is a **token authority**. Any compromise of the signing key, the D1 token table, or the validation cache breaks the security guarantee for every downstream consumer.
37+
38+
| Boundary | Trust assumption |
39+
|----------|------------------|
40+
| `TOKEN_SIGNING_KEY` (Worker secret) | Confidentiality + integrity. Compromise = full forgery capability. |
41+
| D1 `api_tokens` table | Integrity. Holds SHA-256 hashes only; plaintext recovery is infeasible from this table. |
42+
| `AUTH_TOKENS` KV (cache) | Soft state. Stale entries are bounded by 30s TTL; revocation must clear cache. |
43+
| `AUTH_REVOCATIONS` KV | Authoritative for "revoked" decisions on the fast path. |
44+
| Caller-provided tokens | Untrusted until verified end-to-end (signature, hash lookup, revocation check, expiry). |
45+
46+
## Cryptographic Design
47+
48+
- **Signing**: HMAC-SHA256 over canonical token payload, key from `TOKEN_SIGNING_KEY` (256-bit).
49+
- **At-rest storage**: SHA-256 hash of the issued token. Plaintext is **never persisted** server-side.
50+
- **One-time disclosure**: Plaintext token is returned to the caller exactly once at issuance. There is no recovery path; lost tokens must be reissued.
51+
- **Random sources**: Web Crypto `crypto.getRandomValues` / `crypto.randomUUID` only.
52+
- **No password hashing yet**: Registration today issues tokens, not password-backed sessions. Any future password storage must use a Workers-compatible KDF (PBKDF2 via Web Crypto).
53+
54+
## Validation Pipeline
55+
56+
A token is trusted only after all of:
57+
58+
1. Format check (prefix + base64url shape).
59+
2. Signature verification with `TOKEN_SIGNING_KEY`.
60+
3. SHA-256 hash lookup in D1 `api_tokens` (or KV cache for ≤30s).
61+
4. `status === 'active'` and `expires_at > now`.
62+
5. Not present in `AUTH_REVOCATIONS` KV.
63+
6. Rate-limit check against `AUTH_RATE_LIMITS` KV (per-token, 1h window).
64+
65+
Any step failing → reject; never short-circuit later checks.
66+
67+
## Revocation Semantics
68+
69+
- Revocation writes to D1 (`status = 'revoked'`) **and** `AUTH_REVOCATIONS` KV **and** evicts `AUTH_TOKENS` cache entry.
70+
- Code paths that consult only the cache without checking `AUTH_REVOCATIONS` are bugs and must be treated as security regressions.
71+
72+
## Audit
73+
74+
- D1 `audit_logs` records issuance, validation outcome, revocation, and refresh events.
75+
- Logs MUST NOT contain plaintext tokens, signing keys, or full bearer headers. Token references use the `tok_*` ID or hash prefix only.
76+
- `AUTH_AUDIT` KV is a write-buffer — it is not the system of record; D1 is.
77+
78+
## Secret Management
79+
80+
- Canonical secret names are `CHITTYAUTH_ISSUED_MINT_API_KEY` and `CHITTYAUTH_ISSUED_CONNECT_API_KEY` (when used). They are delivered exclusively via `wrangler secret put` and never live in `wrangler.toml`, source, KV, D1, or logs.
81+
- Legacy aliases `TOKEN_SIGNING_KEY` and `CHITTYCONNECT_API_KEY` remain accepted only for migration compatibility and must be removed after cutover.
82+
- Rotation cadence: `CHITTYAUTH_ISSUED_MINT_API_KEY` rotated quarterly. Rotation requires a coordinated reissue window since existing tokens become unverifiable when the key changes.
83+
- 1Password is the cold source of truth per the global ChittyOS operator policy; Cloudflare Worker secrets are the runtime delivery channel.
84+
85+
## Hardening Checklist (per deploy)
86+
87+
- [ ] `wrangler secret list` shows `CHITTYAUTH_ISSUED_MINT_API_KEY` present
88+
- [ ] `wrangler.toml` D1 `database_id` and KV `id` fields are real (no `CREATE_NEW_*` placeholders)
89+
- [ ] `/health` returns `checks.database === true` and `checks.kv === true`
90+
- [ ] No diff in this release introduces plaintext-token logging or new mocked auth paths
91+
- [ ] Rate-limit window and TTL settings unchanged or reviewed
92+
- [ ] Token-prefix scheme (`ca_live_`/`ca_test_`/`ca_dev_`) matches deploy environment
93+
94+
## Known Limitations
95+
96+
1. **No application-level WAF or per-IP throttling** — relies on Cloudflare’s platform protections; per-token rate limiting is the only application-level throttle today.
97+
2. **No automated CI security scanning configured in this repo** — CodeQL, secret scanning, and `npm audit` gates are not part of the workflow set in `.github/workflows/`. Reviewers must perform these checks manually until added.
98+
3. **Single signing key, no kid rotation overlap** — rotating `TOKEN_SIGNING_KEY` invalidates all outstanding tokens. There is no dual-key verify window today.
99+
4. **End-user tokens only** — service-to-service authentication is explicitly out of scope; do not retrofit `chittyauth-app` for inter-service auth.
100+
101+
## Security Contacts
102+
103+
- **Email**: security@chitty.cc
104+
- **GitHub Security Advisories**: https://github.com/CHITTYAPPS/chittyauth-app/security/advisories

0 commit comments

Comments
 (0)