DataTalksClub / DataTalksClub/website
Integrate staff OIDC login and session lifecycle
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 0
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
Parent epic: #7
Normative authority:
- 06 — Studio and admin API: authentication
- 07 — Identity and authorization
- 10 — Verification strategy
- Single durable account
- Resolved staff-identity decision #20
- Open decision 9
Outcome
Integrate the owner-selected shared Cognito path for human Studio login while preserving the one durable accounts.CustomUser identity, deny-by-default Django group/permission authorization, revocable StaffSession boundary, and strict separation from Bearer API credentials.
A successful callback must resolve one approved staff identity to exactly one existing durable account and expose an accounts-owned identity/session lifecycle handoff that #32 and #33 can consume without parsing provider claims or trusting a browser cookie. No callback creates an account, role, permission, editorial Person, or production credential.
Resolved direction and current baseline
- #20 is closed: reuse the existing shared Cognito pool (
us-east-1_H7nJu52Bs,auth.dtcdev.click); Django groups/permissions remain authoritative; genericis_staffis not authorization. - #20 also removed the dedicated break-glass credential workflow. Management-API parity remains tracked by #7, with direct database access only as the last-resort operational fallback if that API is unreachable.
- #86 is closed and merged. It supplies provider-neutral
StaffSession, active-staff and explicit-permission authorization hooks, audit primitives, and deterministic adapters; it does not implement OIDC or session expiry. - #141 is closed and merged for the non-identity security boundary. Its traceability record explicitly leaves OIDC, MFA, identity proof, and session lifecycle here.
- #32 and #33 are downstream consumers, not prerequisites for this protocol implementation. #63 consumes the completed identity evidence for residual threat-model traceability. Superseded #70 is not a dependency.
- The current application has no human staff OIDC start/callback/logout implementation and no accepted provider-bound identity/session handoff.
Owner decisions required before engineering dispatch
The selected provider and no-dedicated-break-glass direction are resolved. The following values are not established by #20 or the normative specifications and must not be inferred by engineering:
- Staff admission and linking: whether first Studio login is restricted to a pre-provisioned local staff account, the exact allowlisted Cognito/IdP claims beyond immutable issuer/subject and verified email, conflict/quarantine behavior requiring operator review, and which owner may provision or offboard staff.
- Session policy: exact idle timeout, absolute timeout, idle-activity refresh rule, maximum offboarding/revocation propagation bound, and whether existing locally valid sessions survive a temporary provider outage until those limits.
- Environment binding: the reviewed non-secret issuer/authorization/token/JWKS/logout authorities, audience/client identifiers and callback/logout paths for each environment, plus secret references/rotation ownership. Secret values never belong in this issue or repository.
The authorized product/identity owner may resolve these directly in a versioned, redacted comment or linked control record on this issue. Keep the decision label and do not dispatch engineering until all three groups are explicit.
Scope after the decision gate
- Add an accounts-owned shared-Cognito Authorization Code flow with PKCE, state, nonce, exact issuer/audience/signature/time validation, one-time callback handling, bounded metadata/JWKS behavior, and fail-closed configuration.
- Resolve immutable provider identity plus adapter-verified email evidence to exactly one eligible existing
CustomUserunder the approved admission/linking policy. Existing links must remain stable across email changes. Zero/multiple owners, conflicting provider links, unverified email, inactive/quarantined/absorbed accounts, missing claims, or identity drift fail generically and create redacted audit evidence. - After identity resolution, re-read the local account and require active staff plus explicit
core.access_studio; provider claims never become Django permissions. Rotate the Django session identifier and bind exactly one activeStaffSessionwithout storing the raw Django session key, OIDC token, authorization code, or provider payload. - Enforce the approved idle and absolute limits on every Studio request through the shared authorization boundary. Logout, local disablement, staff/Studio-access removal, offboarding, explicit revocation, and expiry revoke the affected staff session and prevent browser-back disclosure on the next request or approved tighter bound.
- Supply the downstream identity/session interface consumed by #32/#33: an accepted identity resolves to the durable local user ID plus current
StaffSessionID/authentication time; an accounts-owned current-status/revocation operation accepts only the local user ID and safe reason. It must let downstream code ensure or deactivate that user's one humanAPIPrincipalwithout receiving raw claims, tokens, provider payloads, or editorial identity data. - Deny new login safely during discovery/JWKS/token/provider failure while preserving anonymous public reads. Apply the approved login/callback throttling and operator-visible redacted failure classification.
- Keep every login, callback, logout, denial, Studio, and error response private/no-store/noindex and referrer-safe as applicable. Redirect destinations are bounded same-host paths only.
- Add deterministic provider and clock adapters for repository verification; real provider and MFA validation remains the separately owned
[HUMAN]gate below.
Explicit non-goals
- No dedicated break-glass account, credential, storage, rotation, drill, recovery UI, production Django-admin path, or database-access procedure.
- No learner/social-signup redesign, account creation, duplicate-account reconciliation, profile collection, editorial
Personlinkage, role editor, capability policy, domain management behavior, API-token lifecycle, or OpenAPI work. - No authorization from
is_staff, superuser, email domain, provider group name, identity snapshot, or an MFA claim alone. Local explicit permission remains required. - No interchange between OIDC tokens, Django browser sessions, and admin-API Bearer credentials.
- No provider/AWS/production mutation, real identity creation, secret readback, or production data access during repository engineering or automated verification.
External MFA production gate
DataTalksClub/aws-infra#24 is open. Optional Cognito TOTP covers native Cognito authentication, but the selected clients are Google-federated and Cognito does not impose its native MFA challenge on that upstream session. Production staff login therefore requires accepted, secret-free evidence that the exact Google Workspace/shared-Cognito path enforces MFA before #61 can be closed or described as production-ready.
This external gate does not authorize repository code to infer MFA from an absent/unverified claim. Once the owner decisions above are recorded, deterministic repository engineering may proceed. If all automated gates pass before external evidence exists, delivery uses Refs #61, adds human, and leaves the issue open with the exact remaining check.
Acceptance criteria
-
[DECISION]The three owner-decision groups above are recorded in a redacted, versioned control record; implementation and tests use those exact values. - Production settings fail closed when the required reviewed issuer/audience/client/callback/logout or secret reference is absent, malformed, cross-environment, or unsafe; no secret or provider payload enters logs, audits, errors, screenshots, exports, or retained verification artifacts.
- Login and callback enforce Authorization Code + PKCE, state, nonce, exact issuer/audience/signature/time/one-time-code checks, bounded safe return paths, and generic denial for replay, substitution, mix-up, metadata/JWKS failure, and malformed/extra input.
- Exactly one approved immutable provider identity resolves to exactly one eligible existing durable account using verified ownership evidence. All ambiguity/conflict/unlinked/inactive/quarantined/absorbed/drift cases fail closed without account, role, permission,
Person, principal, or session creation. - Every accepted login re-reads local state, requires active staff and explicit
core.access_studio, rotates the Django session, and binds one revocableStaffSession; raw OIDC/browser credentials and provider payloads are not persisted in that record. - The approved idle/absolute timeout, activity refresh, logout, local disablement, permission removal, offboarding, explicit revocation, fixation, concurrent callback/logout, and browser-back behavior is enforced at the shared Studio boundary with the approved next-request or tighter propagation bound.
- The accounts-owned handoff returns only the durable local user identity and safe session evidence and exposes fail-closed status/revocation behavior sufficient for #32/#33 to ensure/deactivate exactly one linked human
APIPrincipal; raw provider inputs and editorial identity never cross that boundary. - Provider/configuration outages deny new login and emit bounded redacted operator evidence without breaking anonymous public reads or weakening existing-session behavior beyond the approved policy.
- Login/callback throttling, CSRF boundaries, secure host-only HttpOnly SameSite cookies, private/no-store/noindex/referrer-safe responses, safe redirects, and error redaction pass positive and adversarial tests.
- The complete focused and graph-selected Django/PostgreSQL, security, identity/account-linking, compatibility, browser, container, secret-canary, and operational verification is green on one frozen candidate; the independent tester captures and inspects all required desktop/mobile states and PM accepts before commit.
-
[HUMAN]The exact production Google Workspace/shared-Cognito path has accepted, secret-free MFA evidence under DataTalksClub/aws-infra#24. Until then, #61 remains open and production staff login is not accepted.
Django, integration, and concurrency scenarios
- Exercise valid deterministic login plus bad/missing/replayed state, nonce, PKCE verifier, authorization code, issuer, audience, signature, timestamps, verified-email evidence, required claims, metadata/JWKS response, and cross-environment configuration.
- Exercise first link/relogin/email change under the accepted policy; zero/multiple accounts, existing-link mismatch, duplicate provider subject, competing callback, identity-state drift, inactive/quarantined/absorbed user, non-staff user, and missing explicit Studio permission all deny generically without partial state.
- Prove a successful callback rotates the Django session, creates/replaces exactly one active staff-session binding, and persists no raw code/token/cookie/provider payload. Concurrent callback/callback and callback/logout outcomes remain deterministic and fail closed.
- At exact timeout boundaries, exercise idle activity, absolute expiry, logout, explicit one/all-session revocation, account disablement, staff/permission removal, offboarding, password/security invalidation, provider outage, and revoked-session replay. Use separate database connections for relevant races.
- Exercise the downstream handoff with an approved synthetic identity: stable local user/session evidence, current-status lookup, revocation, relogin, and generic denial. Prove #32/#33 can bind one human principal by local user ID while OIDC tokens/sessions and Bearer credentials remain non-interchangeable.
- Prove private headers, CSRF/fixation protections, strict method/input bounds, safe same-host return paths, throttling, bounded/redacted audit and metrics, provider-failure alert classification, and anonymous public-read continuity.
Browser scenarios
Use deterministic synthetic identities only and never capture a token, authorization code, cookie, email, provider subject, or production identity.
- At desktop and mobile widths, start login, complete a valid callback, land on the bounded same-host destination, and verify permitted Studio navigation, keyboard/focus behavior, responsive containment, and private/no-store/noindex responses.
- Render and inspect generic denied states for missing required evidence, unlinked/conflicting/inactive/non-staff identities, missing Studio permission, provider outage, and throttling; no state reveals whether an account or provider identity exists.
- Exercise logout, idle and absolute expiry, local disablement/permission removal, explicit revocation, refresh, back/forward, duplicate callback, and safe return-path rejection. Cached Studio content and identity data must not reappear.
- Exercise an already-established session during a deterministic provider outage according to the exact approved policy while anonymous public pages remain available.
Repository and operational verification
- Generate the versioned selective-CI plan from the exact frozen base/head and classify every component once. Unknown impact or invalid/stale evidence selects a fresh full run.
- Use
uv/Make gates for migration drift, formatting/lint/typecheck, focused and full Django, PostgreSQL concurrency, security/identity/account-linking, compatibility, full Playwright for auth/template/browser-harness impact, container, and secret-canary checks. - The independent tester recomputes the plan and captures/inspects every graph-selected desktop/mobile route/state under
.tmp/screenshots/; no required evidence may remain pending. - External provider/MFA verification is
[HUMAN], separately owned, and secret-free. Repository fixtures cannot satisfy it.
Dependencies and delivery
Satisfied: #20, #86, and the #141 non-identity foundation.
Blocked before engineering: the three explicit owner-decision groups on this issue.
Blocked before production acceptance/closure: DataTalksClub/aws-infra#24 MFA evidence.
Downstream consumers: #32 and #33. Residual traceability consumer: #63. Parent parity/recovery gate: #7.
Follow _docs/PROCESS.md. Engineer work remains uncommitted; a separate tester verifies the exact frozen candidate and screenshots; PM accepts; then the engineer creates a focused commit. Use Refs #61 and keep the issue open while the [HUMAN] MFA gate remains. The orchestrator locally merges with --no-ff, pushes main, and on-call alone observes CI/deployment. No pull request is created.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the linked authentication, identity and authorization, verification, and durable-account specifications, then inspect the provider-neutral StaffSession baseline from #86. Wait for the three required owner decision groups before engineering. Done means the acceptance criteria and deterministic security, identity, concurrency, and integration verification pass, while the external MFA gate remains satisfied separately.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, django, postgresql, python
- Domain
- authentication, authorization, backend, databases, security, testing-qa
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100