Files
mangaBookmark/backend/internal/web/templates/login.html
T
sulthan bcc6b45515 feat(backend): Discord OAuth login with DB-backed sessions (#23) (#31)
Implements #23 per ADR-0002.

- Discord authorization code grant (identify + guilds.members.read), form-encoded token exchange
- Guild membership gate via the single-guild endpoint; optional DISCORD_REQUIRED_ROLE (empty default)
- Owner Discord ID is the only identity allowed to sign in
- Sessions are DB rows with opaque random ids; cookie carries only the id; expiry enforced; delete = revoke
- HMAC session signing, derived key, and WEB_PASSWORD removed; no replacement signing secret
- Login rate limiting preserved on the callback
- Full flow tested through the real router against a local Discord stub (DISCORD_API_BASE)
- Env: DISCORD_CLIENT_ID/_CLIENT_SECRET/_GUILD_ID/_REQUIRED_ROLE/_API_BASE/_REDIRECT_URI; docs updated

go test ./... passes.

Reviewed-on: #31
Co-authored-by: Sulthan Zaki <sultankiki05@gmail.com>
Co-committed-by: Sulthan Zaki <sultankiki05@gmail.com>
2026-08-08 08:51:22 +07:00

33 lines
1.2 KiB
HTML

{{define "login"}}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="color-scheme" content="dark light">
<title>BookmarkManager</title>
<link rel="icon" href="/static/logo.svg" type="image/svg+xml">
<link rel="stylesheet" href="/static/style.css">
<link rel="preload" href="/static/fonts/instrument-serif-400-latin.woff2" as="font" type="font/woff2" crossorigin>
</head>
<body>
<main class="login-card">
<div>
<span class="eyebrow">Private library</span>
<h1 class="brand">{{template "mark" .}}<span>Bookmark<em>Manager</em></span></h1>
</div>
<figure class="login-art" aria-hidden="true">
<img src="/static/login-art.png" alt="">
</figure>
<form method="get" action="/auth/discord">
{{/* The page reloads on a failed sign-in, so the message is present from
the start; role=alert is what gets it announced anyway. */}}
<p class="error" role="alert">{{.Error}}</p>
<button type="submit">Continue with Discord</button>
</form>
<p class="login-note">Guild membership is required to sign in.</p>
</main>
</body>
</html>
{{end}}