auth

package
v0.0.0-...-ddef314 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 6, 2026 License: MIT Imports: 21 Imported by: 0

README

auth

Passwordless authentication module. Issues session cookies via JWT after verifying a 5-digit email code, and provides middleware to gate HTTP handlers.

Routes

  • GET /login - login form (email + optional Discord/Google buttons).
  • POST /login - starts an email login. If no member exists for the email, renders the signup confirmation page instead of sending a code.
  • GET /login/sent - "check your email" page with a 5-digit code form.
  • POST /login/code - submits a code from the form.
  • GET /login/code?code= - one-click login from the email link.
  • POST /login/confirm-signup - creates a new member from a signed signup-confirm token, then completes login per provider (email, discord:<user_id>, google).
  • GET /logout - clears the token cookie and redirects to callback_uri.
  • GET /whoami - JSON dump of UserMetadata for the current session.

Middleware

  • WithAuthn - requires a valid token cookie whose JWT audience is conway and whose subject resolves to a row in members. On failure, redirects to /login?callback_uri=<current URL>. Injects *UserMetadata into the request context (retrieve via GetUserMeta).
  • WithLeadership - wraps WithAuthn and returns 403 unless members.leadership is true.
  • OnlyLAN - returns 403 if CF-Connecting-IP is set (i.e. request came through Cloudflare).

Behavioral details

  • Login codes are stored in the login_codes table, are single-use (deleted on consumption or expiration), and expire after 5 minutes. A background worker (AttachWorkers) sweeps expired rows hourly.
  • Code generation retries up to 3 times on PRIMARY KEY collision, then errors.
  • Code submission is rate-limited globally to 5/sec via a shared rate.Limiter (the limiter blocks rather than rejects).
  • The session JWT has audience conway, subject <member_id>, and a 30-day expiry. The cookie is SameSite=Lax, Path=/, and Secure only when self.Scheme contains "s" (https).
  • On successful login, members.confirmed is flipped to true if it was false.
  • Signup is gated by a separate JWT with audience signup-confirm and a 10-minute expiry. The Issuer claim encodes the originating provider, so the same confirmation flow works for email, Discord (discord:<user_id> - links Discord account on creation), and Google.
  • Cloudflare Turnstile verification is fail-open: if turnstile is nil, the network call fails, or Cloudflare returns >=400, the request is allowed through. Only an explicit success: false response blocks login.
  • DiscordLoginEnabled / GoogleLoginEnabled are nil by default; the discord/google modules set them at wire-time. If nil, the corresponding button is hidden.
  • CompleteLoginForMember is the entry point external OAuth modules use to finalize a session; it mints a short-lived internal JWT and feeds it through the normal completeLogin path.
  • New members are created via INSERT ... ON CONFLICT(email) DO UPDATE SET email=email RETURNING id, so concurrent signup confirmations for the same email are safe.

Documentation

Overview

templ: version: v0.3.1001

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func OnlyLAN

func OnlyLAN(next http.HandlerFunc) http.HandlerFunc

OnlyLAN returns a 403 error if the request is coming from the internet.

Types

type Module

type Module struct {

	// DiscordLoginEnabled is set by the discord module to indicate whether
	// Discord-based login is available. If nil, the Discord login button is hidden.
	DiscordLoginEnabled func(ctx context.Context) bool

	// GoogleLoginEnabled is set by the google module to indicate whether
	// Google-based login is available. If nil, the Google login button is hidden.
	GoogleLoginEnabled func(ctx context.Context) bool
	// contains filtered or unexported fields
}

func New

func New(d *sql.DB, self *url.URL, tso *TurnstileOptions, tokens *engine.TokenIssuer) *Module

func (*Module) AttachRoutes

func (m *Module) AttachRoutes(router *engine.Router)

func (*Module) AttachWorkers

func (m *Module) AttachWorkers(mgr *engine.ProcMgr)

func (*Module) CompleteLoginForMember

func (m *Module) CompleteLoginForMember(w http.ResponseWriter, r *http.Request, memberID int64, callbackURI string)

CompleteLoginForMember creates a session for the given member ID and redirects. This is used by external login providers (e.g. Discord OAuth) to finish a login flow.

func (*Module) RenderSignupConfirmation

func (s *Module) RenderSignupConfirmation(w http.ResponseWriter, r *http.Request, email, provider, callbackURI string)

RenderSignupConfirmation renders the signup confirmation page. This is exported so that OAuth login modules can use it when they detect a new account.

func (*Module) WithAuthn

func (m *Module) WithAuthn(next http.HandlerFunc) http.HandlerFunc

WithAuthn authenticates incoming requests, or redirects them to the login page.

func (*Module) WithLeadership

func (m *Module) WithLeadership(next http.HandlerFunc) http.HandlerFunc

WithLeadership wraps WithAuthn and additionally requires the user to be a member of leadership.

type TurnstileOptions

type TurnstileOptions struct {
	SiteKey string
	Secret  string
}

See: https://www.cloudflare.com/application-services/products/turnstile

type UserMetadata

type UserMetadata struct {
	ID           int64
	Email        string
	ActiveMember bool
	Leadership   bool
}

func GetUserMeta

func GetUserMeta(ctx context.Context) *UserMetadata

GetUserMeta returns the email address set by WithAuth from the request context.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL