idf-auth
Production-ready identity plane для IDF. Magic-link email + HMAC tenant-signed invites + JWT RS256 + JWKs endpoint.
Live production: auth.intent-design.tech (Ubuntu VPS, docker compose + nginx + certbot, 41 integration tests green).
1. Endpoints
| Endpoint | Назначение |
|---|---|
POST /magic-link | Issue nonce → email через Resend |
GET /magic-link/callback?token=<nonce> | Verify, issue RS256 JWT |
POST /invites | Tenant-signed HMAC; create invite + email |
GET /invites/accept?token=<nonce> | Accept invite, issue JWT |
POST /revoke | Tenant-signed; revoke membership |
GET /revocations?since=<iso> | Pull-endpoint для data plane revocation cache |
GET /.well-known/jwks.json | Public keys для verify на data plane |
GET /health, GET /ready | Health checks |
2. Local dev (10 минут)
git clone https://github.com/DubovskiyIM/idf-auth
cd idf-auth
cp .env.example .env
Ключи RS256:
npm run keys:generate
# скопировать PEM в .env как JWT_PRIVATE_KEY_PEM / JWT_PUBLIC_KEY_PEM
Tenant HMAC secret (для invites / revoke):
node -e 'console.log(require("crypto").randomBytes(32).toString("hex"))'
# в .env как TENANT_HMAC_SECRET
Postgres + миграции:
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres:16-alpine
DATABASE_URL=postgres://postgres:postgres@localhost:5432/postgres npm run db:migrate
Старт:
npm run dev # :4000
curl http://localhost:4000/health # → {"status":"ok"}
curl http://localhost:4000/.well-known/jwks.json # → JWKs
3. Deploy в production
Две проверенные опции:
VPS + Docker Compose (наша prod-инсталляция)
См. DEPLOYMENT-vps.md — Ubuntu 24.04, nginx reverse-proxy, certbot TLS, docker compose с локальным Postgres в том же стеке.
Upshot:
# локально — build amd64 image
docker buildx build --platform linux/amd64 -t idf-auth:amd64 --load .
docker save idf-auth:amd64 -o /tmp/idf-auth-amd64.tar
scp /tmp/idf-auth-amd64.tar root@<vps>:/opt/idf-auth/
# на VPS — reload без потери БД
docker load -i idf-auth-amd64.tar && docker compose up -d --force-recreate auth
Fly.io + Neon Postgres
См. DEPLOYMENT.md. Быстрее настраивается, но платный для high-traffic. Подходит для pet projects и ранней альфы.
4. Интеграция с data plane
Data plane (idf-runtime) цепляется одним env-vars-setом:
IDF_AUTH_JWKS_URL=https://auth.intent-design.tech/.well-known/jwks.json
IDF_AUTH_REVOCATIONS_URL=https://auth.intent-design.tech/revocations
IDF_TENANT_HMAC_SECRET=<shared-secret> # для /invites + /revoke
idf-runtime кэширует JWKs (10 min TTL), pull'ит /revocations?since=<last> раз в 60с, отменяет свой local JWT cache.
5. Тесты
npm test # unit + integration, 41 tests
npm run smoke # E2E против spawned server + testcontainer Postgres
Testcontainers на cold-start может висеть 60с — отключить Ryuk:
echo 'ryuk.disabled=true' >> ~/.testcontainers.properties
Архитектурные решения
- RS256 (не HS256) — public key distribution через JWKs позволяет data plane verify без shared secret
- Magic-link, не password — снимает password rotation / breach blast radius; UX ≈ Slack/Notion
- HMAC tenant signatures на
/invites+/revoke— control plane подписывает, auth verify'ит; избегаем shared DB между control и identity plane - Nonce TTL = 15 минут — короткий enough чтобы phishing-email с просроченным linkом не работал, длинный enough для пользователя прочитать письмо
Ссылки
- idf-auth репо
- DEPLOYMENT.md — Fly.io + Neon
- DEPLOYMENT-vps.md — VPS + self-hosted Postgres