Files
mangaBookmark/docs/adr/0002-discord-oauth-no-passwords-no-email.md
T
sulthan 7a0c190ebe docs: record the domain model and the Postgres/OAuth ADRs
Written while scoping #18. CONTEXT.md pins the ubiquitous language
(Series, Reader, Bookmark, Progress, Latest Chapter, Poll) that the
schema split and the ordering rule are argued in; the four ADRs record
the decisions that follow from it, starting with Postgres over SQLite.

Refs #18
2026-08-08 06:43:38 +07:00

2.6 KiB

Identity comes from Discord OAuth; we store no passwords and send no email

Status: accepted

The service is being published to a community that already lives on Discord, and we have no transactional email infrastructure. Rather than build email verification and password reset to get accounts, Readers sign in with Discord OAuth2 (authorization code grant, identify + guilds.members.read), and guild membership replaces both the invite gate and the email-verification step. No password is ever stored and no mail is ever sent.

Considered options

Email + password with invite codes, no verification. Viable and dependency-free: an invite code proves community membership, which is what email verification was standing in for anyway. Rejected because it still requires password hashing, a manual admin-driven reset path, and a credential store — all of which Discord removes.

Email + password with a transactional provider (Resend, Brevo). Rejected as premature: it builds verification and self-serve reset before anyone has asked for them, and adds deliverability as an operational concern.

Discord OAuth. Chosen. It is less code than either alternative — no hashing, no reset flow, no invite table — and the authorization question ("is this person in my community?") is answered by the same call that answers the authentication question.

Consequences

  • Availability is now coupled to Discord. If Discord's OAuth endpoint is down, nobody can start a new session. Existing sessions are unaffected, which bounds the blast radius.
  • Identity is a Discord snowflake. Migrating off Discord later means re-identifying every Reader, because we hold no other credential for them. This is the lock-in the decision buys, and it is the reason this ADR exists.
  • guilds.members.read is checked at login, not continuously. Someone who leaves the guild keeps their session until it expires. Acceptable; revocation is a session delete, not an architectural change.
  • The userscripts cannot use OAuth. They run in an isolated world on third-party pages with no redirect surface, so they keep a bearer token — now issued per Reader by the backend rather than a single shared API_TOKEN literal. OAuth gates the web UI; the web UI is where a Reader obtains their personal userscript.
  • WEB_PASSWORD disappears, and with it the session HMAC key derivation (sha256(API_TOKEN | WEB_PASSWORD | …)), which needs a replacement secret.
  • Seeding the first Reader during migration requires knowing the owner's Discord user ID up front — a stable snowflake, copied from the Discord client.