Email Infrastructure
OutlookID
A self-hosted email service that lets a single application read, send, and organize mail across many users' Outlook mailboxes through Microsoft Graph, delivering a signed webhook on every new message. Built with Fastify, Drizzle ORM, PostgreSQL, and a pg-boss job queue.
Overview
Connecting an app to many users' Outlook inboxes means handling OAuth per user, rotating tokens, renewing Microsoft Graph subscriptions, and reliably delivering each new email as an event. OutlookID handles all that plumbing so a product team doesn't have to — they connect once, and from then on they get a signed webhook on every new message.
What I built
Designed and built the entire system from scratch — OAuth flows, token encryption, Microsoft Graph subscription lifecycle, webhook delivery pipeline, database schema, and Docker deployment.
Wrote integration tests against real ephemeral Postgres instances using Testcontainers, and unit tests for all cryptographic and token logic.
Authored the README, OpenAPI spec, and deployment guide — so another team could pick this up and run it without needing to read the source.
Architecture
A downstream application calls a single endpoint to start the OAuth flow for a user. OutlookID handles the redirect, exchanges the auth code, encrypts the tokens, and immediately sets up a Microsoft Graph subscription on that mailbox. From then on, Graph pushes change notifications to OutlookID, which validates them and queues a delivery job. The job system retries with exponential backoff for up to 6 attempts before moving to a dead-letter queue that operators can replay.
Under the hood
OAuth tokens are encrypted at rest with AES-256-GCM using a random initialization vector per token — so even if someone pulls the database, they get ciphertext rather than credentials that work.
Concurrent token refresh is coalesced: if two requests hit the same mailbox while its token is expired, they share one refresh round-trip instead of racing and invalidating each other. This prevents the hard-to-debug double-refresh bug that shows up in multi-tenant systems.
Outbound webhooks are signed with a Stripe-style HMAC header — the downstream app can verify every delivery is genuine and hasn't been tampered with, and exponential backoff with dead-letter management means no event is permanently lost.
Microsoft's MSAL library doesn't expose refresh tokens in its standard result — so I had to deserialize its internal token cache to extract the secret by account ID. A necessary workaround, well-tested and documented.
What I learned
Infrastructure libraries (like MSAL) have undocumented behaviors that only reveal themselves at the edge cases — reading the source was the only way to understand what was happening with token caching.
Building the dead-letter queue and retry admin endpoints from the start — rather than adding them after a real failure — was the right call. You need observability before you need it.
Built with
Backend
- Node.js 20+
- TypeScript 5
- Fastify 4
- Zod
- Pino
Data & Queue
- PostgreSQL 14+
- Drizzle ORM
- drizzle-kit
- pg-boss 9
Integrations
- @azure/msal-node 2
- @microsoft/microsoft-graph-client 3
- Google OAuth2
- Gmail API
- Node.js crypto
Infra & Testing
- Docker
- Caddy
- Vitest
- Testcontainers
- nock
- pnpm