DataTalksClub / DataTalksClub/website

Implement OAuth-preferred and email-only verified event registration

Open
#46 8 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

accessibility auth decision email enhancement events frontend integration operations P0 security testing
Dominant language
Python
Stars
0
Forks
0
PR merge metrics
No merged PRs in 30d

Description

Parent epic: #6

Resolved product decisions: #17, #18, #22, #23, and #227
Normative authority:

Current disposition and start gate

This is the product owner for native verified Event registration, but it is BLOCKED before engineering. The former decision blockers are resolved; the remaining blockers are concrete implementation interfaces:

  1. accepted/integrated #45 Event lifecycle, registration-window, schedule, cancellation, and revision services on the numeric public Event identity;
  2. accepted/integrated #49 atomic EmailDelivery/durable-job service, backed by accepted Relay #1, #2, and #3 contracts and the exact pinned consumer boundary required by #49; and
  3. a non-circular #46/#227 interface decision defining the canonical preference subject for an accountless recipient, the initial event-email preference for a first registration, and the preference revision/lifecycle hooks used by registration creation, reactivation, account association, cancellation, and delivery suppression.

The third gate is unresolved. The accepted policy says event email is optional and that disabling it suppresses every Event message, including verification, but it does not define the initial preference state or canonical preference owner for a new accountless address. Engineering must not guess enabled/disabled, create a second preference store, or make normalized email a parallel website identity.

No source-only/model-only/parser-only child is valid on the current interfaces. A dormant registration row would encode lifecycle and preference ownership ahead of #45/#227; a token-only slice has no accepted registration version/revocation owner; and an email-only slice would bypass #49/Relay. Do not file or dispatch such a child until the three gates above are recorded.

Product outcome

Provide both Event-registration paths resolved by #17:

  • OAuth (Google/GitHub), preferred: consume the accepted accounts/social-auth identity boundary and confirm immediately from verified provider/account evidence.
  • Email-only fallback: create/reactivate a pending registration, show a deliberately uniform response, and confirm only after a single-use emailed verification link is submitted.

Neither path creates a learner/course account as a registration side effect. Course registration remains the separate durable-account + verified-email + completed-profile flow. Event registration never creates, merges, or links accounts by normalized-email equality alone.

Route and presentation contract

The canonical route is /events/<positive-public-id>/<current-title-slug>/register, with no trailing slash. The slash form permanently redirects one hop to the current numeric-ID/current-slug route while preserving the raw query. The public ID selects the Event; the title slug is cosmetic; UUID is management-only; an unknown/ineligible Event is 404 or the accepted unavailable state without identity disclosure. Internal actions, verification/management resume destinations, metadata, and links emit only the clean current route.

The ordinary public page uses the shared content-page shell. It supports keyboard, focus/error-summary, no-JavaScript, zoom/reflow, mobile, private/no-store token and management states, and never places an email, token, registration ID, provider/social identifier, or protected value in analytics, logs, referrers, screenshots, or public metadata.

Registration identity and lifecycle

events owns EventRegistration. It has one logical row per (event, normalized_email) with database-enforced uniqueness and retains the original email only under the approved retention policy. It records a UUID, Event reference, status, one monotonic registration/reactivation version, timestamps, optional display name/timezone, privacy-notice evidence, separate optional marketing/newsletter-consent evidence, bounded acquisition data, and short-lived abuse data.

An optional durable-account reference is association evidence only. It may be set only from an explicitly authenticated, accepted, verified account/provider flow. It never changes the (event, normalized_email) logical identity, auto-merges two registrations, treats a public content.Person as an account, or links a pending email-only registration merely because an account has the same normalized email. Conflicting, quarantined, inactive, absorbed/broken, multiple-candidate, provider/email-change, or ownership-mismatch cases fail closed through the accepted accounts identity service.

Lifecycle is pending_verification -> confirmed -> cancelled, plus expired, attended, and no_show where applicable. Re-registration reactivates the same logical row and advances the registration version exactly once. Cancellation and explicit token revocation advance/rotate the owning version as required by the accepted service contract. Verification and management tokens are high-entropy, digest-only, purpose/event/registration/version scoped, expiring, single-use/replay-safe, and never plaintext at rest. GET is non-destructive; confirmation and cancellation require CSRF-protected POST.

The registration version is not a substitute for #45's Event revision, #227's preference revision, or #49's request-hash version. The pre-start handoff must name each owner and the exact scalar crossing each boundary. Delivery keys include the stable registration UUID, registration version, purpose, and the immutable routing inputs required by #49; changed work conflicts rather than being sent under the same key.

Preference, delivery, privacy, and migration boundary

  • Event email is a separate optional category evaluated for the relevant active registration before every Event delivery. Once #227 supplies the accepted subject/default/revision service, HTML, API, imports, registration, and #49 submission all use that one service. No local Event-only toggle or Datamailer adapter is introduced.
  • If the accepted preference permits the message, registration state, one logical #49 EmailDelivery, and its durable job commit atomically. Relay is contacted only by a leased job after commit. If preference suppresses delivery, no intent/job/provider call is created. There is no direct Relay, SES, Datamailer, Mailchimp, or provider call in a request transaction.
  • Verification, confirmation, cancellation, calendar, reschedule, and reminder purposes remain distinct from marketing/newsletter consent. Sponsor evidence under #111 and newsletter behavior remain separate versioned purposes; neither is inferred from registration or OAuth.
  • Uniform submission/resend responses cover new, duplicate, pending, confirmed, cancelled, expired, rate-limited, and unknown addresses without revealing existence or status.
  • Pending unverified registration data expires after the approved 14-day period; Event registration PII follows the approved 90-days-post-event policy and accountless rights/erasure behavior. Delivery/audit retention remains owned by #49/#23.
  • #112 historical aggregates contain no attendee identity and never seed, match, activate, cancel, or alter a native registration, account, preference, newsletter/sponsor evidence, or delivery. Imports/rehearsals are send-disabled and use synthetic or explicitly authorized protected sources only.
  • There is unconditionally no public attendee identity, card, name, initial, avatar, profile, directory, search, list, or export. #112 may expose only its separately accepted non-identifying exact N registered aggregate.

Acceptance criteria

  • The exact accepted #45, #49/Relay, and #227 subject/default/revision interface identities are recorded before engineering starts; no circular dependency or guessed default remains.
  • The numeric public-ID/current-slug route, direct slash redirect, missing/ineligible state, internal destinations, shared shell, private headers, and accessibility contract pass.
  • OAuth-preferred registration confirms only from accepted verified identity evidence; email-only registration remains pending until link verification; neither path creates/merges a learner account or infers account ownership.
  • Database uniqueness, transaction locking, and conflict recovery yield one logical registration per Event/normalized email across duplicate, replay, and concurrent requests.
  • New, pending, confirmed, cancelled, expired, rate-limited, and honeypot outcomes are enumeration-resistant; reactivation advances one registration version and invalidates old verification/management authority.
  • Token entropy, digest-only storage, purpose/Event/registration/version scope, expiry, revocation, replay, redaction, scanner-safe GET, and CSRF POST behavior pass.
  • Privacy notice, optional marketing/newsletter consent, #111 sponsor evidence, Event preference, and any future public-display purpose are separate; no consent or identity publication is inferred.
  • Accepted #227 preference initialization and eligibility are applied atomically; suppression creates no delivery/job/provider work and never mutates unrelated categories.
  • Permitted business state plus one #49 logical delivery and durable job commit atomically; rollback creates none, and retry/concurrency converge without direct network work or ambiguous automatic resend.
  • Retention/erasure, redaction, no-cache/referrer, safe audit, and zero public/private-value leakage pass for both registration paths.
  • Historical aggregate/import replay changes no native registration, account, preference, consent, or delivery, and no attendee identity becomes public.
  • Focused Django, migration, concurrency/security/redaction, API/service-parity, and graph-selected Playwright/container checks pass through a current versioned verification plan and independent tester gate.

Required service and browser scenarios

  1. New, duplicate, concurrent, replayed, pending, confirmed, cancelled, reactivated, expired, attended/no-show, closed-window, cancelled-Event, stale Event revision, and database rollback.
  2. OAuth verified success plus missing/unverified provider email, account collision/quarantine/inactivity/alias failure, signed-in account/email mismatch, and email-only fallback; assert no automatic account creation/link/merge.
  3. Token tamper, wrong Event/registration/purpose/version, expiry, replay, revocation, link-scanner GET, CSRF POST, enumeration, rate limit, and honeypot.
  4. Accepted event-preference initial state, later opt-out/in, stale preference revision, account association/reactivation, suppression at registration and claim, and zero cross-category mutation.
  5. Transaction rollback, worker outage, exact Relay replay/conflict, response loss, provider ambiguity, callback/reconciliation, and cancellation/reschedule/calendar idempotency through #49 fakes only.
  6. Historical aggregate/import coexistence, retention expiry, accountless export/erasure, and zero newsletter/sponsor/public-identity inference.
  7. Independent Playwright at about 1440x900 and 390x844, with JavaScript on/off as selected: Event detail -> register, OAuth-preferred and email-only paths, uniform response, verification POST, confirmation/calendar, management screen, POST cancellation, expired/tampered and generic denial states, keyboard/focus/error summary, zoom/reflow, back/refresh, and inspected screenshots containing no token or PII.

Dependencies and sequencing

  • #17, #18, #22, and #23 are resolved decisions; they are no longer open blockers.
  • #31 and #35 are accepted generic foundation/compatibility inputs.
  • #45 is the hard Event lifecycle/service prerequisite. Its stale slug-only issue wording does not override the accepted numeric route.
  • #49 is the hard delivery prerequisite. Relay #1 -> #2 -> #3 and the exact consumer/deployment gate remain owned there; this issue grants no Relay credential, provider, or production authority.
  • #227 is the hard preference-policy implementation partner. PM must first close the accountless-subject/default/revision seam without inventing a second identity or store; #46 then supplies the accepted native registration identity consumed by #227.
  • #111 may extend the private form only after this base interface is accepted. #112 remains aggregate-only and independent of native attendee identity.

After those gates are accepted and integrated, PM rechecks current main and records exact model/service/migration symbols and merge SHAs in a fresh isolated engineering handoff. Until then, keep this issue open, blocked, and unassigned.

Explicit non-goals

No capacity/waitlist or partial capacity field; no course registration/profile/Slack flow; no public attendee identity/list/export; no content.Person identity inference; no guessed Event-email default; no second account/contact/preference store; no historical-attendee import; no Studio/admin Event CRUD; no template renderer/catalog; no direct Relay/SES/Datamailer/Mailchimp/provider path; no production credential, protected-data access, external send, deployment, or infrastructure mutation.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with _docs/specs/05-events-registration-email.md, _docs/specs/01-platform-architecture.md, _docs/specs/07-security-privacy-operations.md, _docs/specs/09-migration-rollout-roadmap.md, _docs/specs/10-verification-strategy.md, and _docs/PROCESS.md. Resolve and record the #45, #49/Relay, and #227 interfaces before engineering; done requires the listed Django, migration, security, concurrency, API, and graph-selected Playwright checks to pass through the independent tester gate.

Written by the indexing model from the issue text.

Assessment

Tech stack
django, playwright, python
Domain
api, authentication, backend, databases, security, testing
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.