Skip to content

Integrate DataOps with the shared Cognito browser login #119

Closed

Description

@alexeygrigorev

Integrate DataOps with the shared Cognito browser login

Status: blocked — source is accepted, merged, and deployed; one HUMAN Google account-class check remains
Tags: enhancement, portal, frontend, backend, infra, testing, P1
Depends on: HUMAN access to one real @datatalks.club Google account and one real out-of-domain Google account
Blocks: None
Next owner: HUMAN identity verifier
Source owner if the HUMAN check fails: Orchestrator files a new bounded auth bug from sanitized evidence
Resume condition: verify the two real Google account classes against a steady-state deployed DataOps portal, then PM closes #119 without a source commit

Final source disposition

The shared Cognito relying-party integration is complete.

Accepted source history, all ancestors of current source and origin/main:

  • fbcbc1a / merge e562000 — Cognito authorization-code + S256 PKCE, token/JWKS validation, local User mapping, opaque server-side browser sessions, logout, machine-credential isolation, SAM parameters, and OIDC deployment contract.
  • 393a738 / merge 2dc413a — scoped production Cognito configuration to the deploy job so browser tests remain deterministic.
  • 15643d9 / merge e6d3b25 — fixed production cookie bootstrap; the canonical portal uses the HttpOnly browser session and cannot fall back to the retired password UI.

Tester and PM accepted the implementation and both hotfixes. On-Call deployed e6d3b25, verified the live authorize/callback/error/logout seams, and recorded a successful real hosted-password login, local User mapping, secure cookie, logout, and session-reuse denial.

Current canonical source still implements the accepted contract in:

  • backend/src/auth/browserAuth.ts
  • backend/src/docs/portal.ts
  • backend/src/router.ts
  • backend/src/db/sessions.ts
  • backend/src/db/users.ts
  • infra/template.full.yaml
  • .github/workflows/deploy-dataops-v1.yml

There is no agent-verifiable code, test, infrastructure, migration, data, or deployment task remaining under #119.

Current authentication contract

  • Signed-out pages redirect through local /login to the shared auth.dtcdev.click authorization endpoint.
  • Authorization uses fresh state, nonce, and S256 PKCE. The one-time transaction is short-lived and server-side.
  • Callback exchange validates the ID-token signature and issuer/JWKS, DataOps client audience, expiry/not-before, nonce, token use, normalized email, and verified-email claim.
  • Exactly one existing enabled DataOps User must match the verified email. Unknown, disabled, or ambiguous users fail closed; there is no auto-provisioning.
  • A successful browser login creates a bounded opaque server-side session. The browser holds only an HttpOnly; Secure; SameSite=Lax; Path=/ cookie.
  • /api/me and route authorization derive identity from the verified browser session or separately verified API/device credential, never from a raw caller identity header.
  • Pages require the browser session; unauthenticated APIs return JSON 401. The retired production username/password form and /api/auth/login are unavailable.
  • Logout deletes the server-side session, expires the cookie, and redirects through the registered Cognito logout URI.
  • Independent machine/webhook/device/API credentials retain their own route-specific checks and never become browser sessions.
  • No Cognito client secret, Google secret, token, code, verifier, or session value belongs in this repository, GitHub Actions, logs, screenshots, or issue evidence.

Completed acceptance evidence

  • Agent-verifiable Cognito/PKCE, callback, JWT/JWKS, replay, local-user, session, logout, page/API, machine-credential, SAM, and OIDC contracts passed focused and full review.
  • The inbound identity-header exploit and multi-page normalized-email uniqueness gap found by Tester were fixed and independently retested.
  • PM-requested return-target/open-redirect and callback-error privacy/UX gaps were fixed and independently retested.
  • Production CI environment scoping was fixed, accepted, and deployed.
  • Production canonical-portal cookie bootstrap was fixed, accepted, and deployed.
  • On-Call verified live authorization parameters, fresh PKCE/state/nonce, secure transaction cookie, clean callback failure, branded error recovery, API 401, Cognito logout, TLS, and deployed configuration without recording secret material.
  • A real administrator-created hosted-password identity completed login, mapped to an enabled DataOps User, rendered the workspace, logged out, and could not reuse the old session.
  • Shared-auth infrastructure exposes Google for the DataOps client; the deployed domain guard passed synthetic in-domain allow and out-of-domain reject probes.
  • [HUMAN] A real @datatalks.club Google account completes the hosted Google flow and maps to its existing enabled DataOps User.
  • [HUMAN] A real Google account outside @datatalks.club is rejected and receives no DataOps browser session.

HUMAN verification procedure

This issue authorizes no automated credential use or provider action. An authorized human performs the check in a clean/private browser and records only sanitized results.

In-domain allow

Given an existing enabled DataOps User whose normalized email matches the authorized real @datatalks.club Google account
When the human opens https://ops.dtcdev.click, chooses Google at the shared hosted login, and completes the provider flow
Then the callback returns to a clean DataOps URL, the canonical workspace renders, and /api/me represents the matching local User

Out-of-domain deny

Given a real Google account outside @datatalks.club
When the human attempts the same hosted Google flow
Then shared auth/domain enforcement rejects it, DataOps creates no browser session, protected API access remains 401, and the portal exposes no protected data or account-enumeration detail

Logout sanity

Given the successful in-domain session
When the human signs out and revisits a protected route
Then the old session cannot authorize the portal and the browser starts a fresh login

Evidence boundary

Record:

  • UTC timestamp;
  • deployed DataOps source identity if visible without an AWS call;
  • pass/fail for in-domain allow, out-of-domain deny, and logout reuse;
  • sanitized browser destination/status and whether the correct local display identity appeared;
  • a private or sanitized screenshot reference, if useful.

Do not record or paste account addresses, provider profile data, passwords, authorization codes, state, nonce, PKCE values, JWTs, cookies, session IDs, request headers, private screenshots, or exact provider error payloads.

If either account-class check fails, leave #119 open and file a new needs grooming auth bug with the sanitized stage and observed safe behavior. Do not change Cognito, IAM, Lambda, users, sessions, or provider configuration from this tracker.

#166 publication sequencing

#119 source was merged to origin/main and deployed before #166; it is already an ancestor of the repaired #166 rollout chain. #119 must not add a commit to, modify, or be used to bypass the reviewed A/B/C/D cutover artifact.

The HUMAN Google check may run against the current steady-state live portal before #166 begins. Once any #166 transition phase is dispatched, pause this check until Phase D, canonical evidence, and accepted cleanup restore ordinary steady-state deployment. A pass against unchanged #119 auth source remains valid; any auth/config/source change after the pass requires ordinary On-Call regression evidence under its owning issue.

#166 does not reopen #119 implementation and is not a dependency for the source-complete tracker.

Historical evidence

Out of scope

  • Any additional source implementation, repository verification, deployment, AWS/shared-auth mutation, provider configuration, credential handling, User/session/data mutation, or import/export/restore work.
  • Auto-provisioning, Cognito-group role sync, broad user lifecycle, or changing route-specific machine credentials.
  • Treating synthetic domain-guard evidence as the two-account HUMAN pass.
  • Repeating the real hosted-password test.
  • Closing Integrate DataOps with the shared Cognito browser login #119 without both real Google account-class results, or treating Replace the transitional Tasks table with the final cardId schema #166 transitional deployment as authorization to run them.

Activity

  1. added
    enhancementNew or improved functionality
    needs groomingRaw intake that needs PM grooming
    infraDeployment and infrastructure
    P1Important
    portalShared portal shell and UX
    on Jul 14, 2026
  2. changed the title [-]Replace portal Basic auth with shared dtcdev Cognito login[/-] [+]Integrate DataOps with the shared Cognito browser login[/+] on Jul 14, 2026
  3. added and removed
    needs groomingRaw intake that needs PM grooming
    on Jul 14, 2026
  4. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    Software Engineer handoff

    Implementation is ready for Tester review in the uncommitted worktree
    /home/alexey/git/dataops/.tmp/worktrees/worktree-issue-119 on branch
    worktree-issue-119.

    Implemented

    • Added Cognito authorization-code login with S256 PKCE, random state/nonce,
      a short-lived one-time DynamoDB transaction, and a separate HttpOnly binding
      cookie. Transaction records store hashes rather than the browser state or
      binding value.
    • Added token exchange and RS256 ID-token verification against cached issuer
      JWKS, including refresh on missing/rotated keys or signature failure;
      validates algorithm/key use, signature, issuer, audience, expiry, not-before,
      nonce, token use, email, and email verification.
    • Maps normalized verified email to exactly one existing enabled DataOps user.
      Unknown, disabled, or ambiguous identities receive the same non-sensitive
      403 and are never provisioned.
    • Added cryptographically random, opaque server-side browser sessions with
      absolute expiry and DynamoDB TTL. The cookie is
      HttpOnly; Secure; SameSite=Lax; Path=/; old bearer sessions without the new
      expiry fields retain their prior contract.
    • Propagates the real local user ID/role through /api/me and existing route
      authorization instead of the browser portal-admin fallback.
    • Added local revocation plus Cognito logout. Reusing the deleted cookie fails.
    • Removed the production Basic/password-derived browser gate and old login
      form; /api/auth/login remains unavailable in portal mode. Basic credentials
      cannot serve a browser page or authorize a portal API.
    • Preserved dedicated email-document intake before browser auth, existing
      bearer clients, portal-broker credentials, and route-specific layered
      authorization. Bookkeeping retains both bearer-session and ingestion-key
      checks in portal mode. Bookkeeping, calendar, and newsletter importers now use
      their existing bearer-session option instead of the removed Basic path.
    • Added non-secret CloudFormation parameters/env for auth base, user pool,
      issuer, JWKS, client, callback, logout, and session lifetime. No Cognito client
      or Google secret is present, and no new AWS permission was added.
    • Updated the GitHub Actions OIDC deploy workflow to pass every auth parameter
      to dataops-v1; it does not deploy or mutate dtcdev-shared-auth.
    • Updated auth/runtime/importer documentation and infrastructure contract
      assertions.

    Changed files

    • Auth/runtime: backend/src/auth/browserAuth.ts,
      backend/src/db/sessions.ts, backend/src/db/users.ts,
      backend/src/docs/portal.ts, backend/src/router.ts,
      backend/src/types.ts
    • Machine clients: backend/scripts/import-bookkeeping.ts,
      backend/scripts/import-calendar.ts,
      backend/scripts/import-newsletter-slots.ts
    • Tests: backend/tests/browser-auth.test.ts,
      backend/tests/docs-portal.test.ts, backend/tests/portal-auth.test.ts,
      backend/tests/api-bookkeeping.test.ts,
      backend/tests/import-bookkeeping.test.ts,
      backend/tests/newsletter-slots.test.ts, backend/tests/calendar.test.ts,
      backend/tests/bookkeeping-production-frontend.test.ts,
      tests/infra/test_template.py
    • Deployment: infra/template.full.yaml,
      .github/workflows/deploy-dataops-v1.yml
    • Docs: docs/architecture.md, docs/calendar.md,
      docs/local-development.md, docs/v1-runtime-architecture.md

    Verification run by SWE

    • npm --prefix backend test — exit 0; 1,190 tests passed, 0 failed.
    • npm --prefix backend run typecheck — exit 0.
    • npm --prefix backend run build — exit 0.
    • npm --prefix backend run test:e2e — exit 0; 342 Playwright tests passed,
      0 failed.
    • uv run --with pytest python -m pytest tests/infra/test_template.py — exit
      0; 19 tests passed, 0 failed. This includes exact GitHub OIDC and all eight
      shared-auth deploy-parameter assertions.
    • make sam-validate — exit 0; infra/template.full.yaml is a valid SAM
      template.
    • make sam-build — exit 0; BackendFunction package built successfully.
    • Focused auth/portal/machine regression run — exit 0; 63 tests passed, 0
      failed.
    • git diff --check — exit 0.

    Acceptance status / remaining checks

    • The first 13 implementation and automated-verification acceptance criteria
      are checked in the issue body.
    • Tester must independently rerun the mandated commands and capture/read the
      signed-out auth-error and authenticated workspace screenshots. SWE did not
      claim the Tester-only checkbox or create review screenshots.
    • No application or shared-auth stack was manually deployed. Deployment remains
      exclusively through the GitHub Actions OIDC workflow after approved merge.
    • Both [HUMAN] hosted-login checks remain unchecked. After deployment, verify
      a real administrator-created Cognito password user, logout/reuse denial, an
      allowed @datatalks.club Google account, and rejection of a Google account
      outside the domain without recording credentials, codes, tokens, or session
      values.
    • The issue should receive the human label and remain open after code ships
      until those checks are recorded.

    No commit or push was made.

  5. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    CHANGES REQUESTED

    Independent Tester verification found one exploitable authorization failure and one incomplete uniqueness boundary. The implementation must return to SWE before PM acceptance. I did not commit, push, deploy, or check the Tester acceptance box.

    Blocking security finding

    In production portal mode, /api/me trusts the inbound x-user-id header before validating a bearer session:

    const authenticatedUserId = headerValue(event.headers, 'x-user-id') || portalUserId;

    handlePortal deliberately allows a request carrying any bearer-shaped header to continue to the legacy bearer validator. That means a caller can send a fabricated bearer value plus the ID of an existing user and receive that user's profile without a valid cookie, bearer session, or broker secret.

    Reproduction (local in-memory tables, portal mode, no auth bypass):

    GET /api/me
    Authorization: Bearer fabricated-token
    x-user-id: victim-admin
    

    Actual: HTTP 200 with the victim-admin admin record.

    Expected: HTTP 401. A valid bearer session with a mismatched x-user-id must resolve to the bearer session's user, never the caller-supplied header.

    The reproduction command exited 0 and printed:

    {"statusCode":200,"body":"{\"user\":{\"id\":\"victim-admin\",...\"role\":\"admin\"}}"}

    Required fix: derive /api/me identity only from (a) handlePortal's verified browser userId, (b) a broker identity after the portal-secret check, or (c) a successfully loaded bearer session. Do not treat a raw inbound x-user-id as authenticated. Add regressions for a fabricated bearer plus x-user-id, and for a valid bearer plus a different x-user-id.

    Additional identity-mapping gap

    getUsersByNormalizedEmail performs only one DynamoDB ScanCommand. DynamoDB Scan pages at 1 MiB, so a matching or duplicate user beyond LastEvaluatedKey is ignored. That does not prove the acceptance requirement that the verified email maps to exactly one existing user across the table. Paginate to exhaustion (or use an indexed normalized-email key) and add a multi-page duplicate regression.

    Required command evidence

    • npm --prefix backend test — exit 0; 1,190 tests, 1,190 passed, 0 failed.
    • npm --prefix backend run typecheck — exit 0.
    • npm --prefix backend run build — exit 0.
    • npm --prefix backend run test:e2e — exit 0; 342 Playwright tests passed, 0 failed.
    • make sam-validate — exit 0; infra/template.full.yaml valid.
    • make sam-build — exit 0; BackendFunction build succeeded.
    • uv run --with pytest python -m pytest tests/infra/test_template.py — exit 0; 19 passed, 0 failed.
    • Focused auth/portal/machine regression command (browser-auth, docs-portal, portal-auth, bookkeeping, calendar, and newsletter suites) — exit 0; 63 passed, 0 failed.
    • git diff --check — exit 0.

    The green suites do not cover the spoofed-header case above, so they do not establish the affected acceptance criteria.

    Screenshot evidence

    • .tmp/screenshots/issue-119/signed-out-auth-error.png — 1440x900, SHA-256 5e74d7ffe504e4a5cd755e8afb67d9de8606aff440783fb75937a90c795765b7; inspected: HTTP 400 safe sign-in error, no 404, overlap, credential, code, token, or session value.
    • .tmp/screenshots/issue-119/authenticated-workspace.png — 1440x900, SHA-256 6f9e1d3424ba2e107311b95e29793a450f2a33e343850a2e123819aaf2f743be; inspected: authenticated Tasks / Work Queue empty state, /api/me returned the seeded local admin, no 404, error page, overlap, or leaked credential.

    The screenshot harness also verified signed-out / -> 302 /login, signed-out /api/tasks -> JSON 401, callback-without-state -> safe 400, authenticated workspace -> 200, and authenticated /api/me -> the mapped local user. The harness itself exited 0.

    Acceptance-criterion assessment

    • Authorization start, callback validation, PKCE/state/nonce, JWT/JWKS checks, sessions/logout, page/API separation, Basic-auth removal, machine credential isolation, stack parameters, OIDC deploy wiring, and visual states have positive automated evidence.
    • The real-local-user and credential-isolation criteria are not accepted because /api/me can be spoofed as described.
    • Exact-one-user mapping is not accepted because the scan does not paginate.
    • Both real hosted-login criteria remain [HUMAN] and were not attempted by Tester.

    Please send the fixes back for an independent Tester rerun.

  6. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    Software Engineer retest handoff

    The Tester-requested changes are implemented in the uncommitted worktree
    /home/alexey/git/dataops/.tmp/worktrees/worktree-issue-119 on branch
    worktree-issue-119.

    Changes made

    • Removed the raw x-user-id trust path. Outside the explicit test-only auth
      bypass, every casing of this inbound header is deleted before authorization
      and is replaced only after browser-session, signed portal-broker, or bearer-
      session verification.
    • /api/me now resolves and returns an existing enabled DataOps user only from
      a verified browser/broker identity or a valid bearer session. A fabricated
      bearer plus an existing user ID is rejected.
    • Preserved valid legacy bearer sessions in portal mode while ensuring a
      mismatched client-provided x-user-id cannot override the session user.
    • Made normalized-email lookup scan every DynamoDB page before deciding whether
      there is exactly one match, so identities on later pages are found and
      duplicates split across pages still fail closed in the callback flow.
    • Added regression coverage for /api/me, another protected API route, bearer
      identity precedence, later-page matches, and cross-page ambiguity.

    Verification after the fixes

    • Focused auth/user regression run: 32 tests passed, 0 failed.
    • npm --prefix backend test: 1,195 tests passed, 0 failed.
    • npm --prefix backend run typecheck: passed.
    • npm --prefix backend run build: passed.
    • git diff --check: passed.

    No UI or infrastructure files changed as part of this retest fix, so the prior
    green E2E (342/342), infrastructure tests (19/19), SAM validation, and SAM build
    remain applicable. No commit or push was made.

  7. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    TEST PASS

    The two previously blocking findings are fixed and independently verified. All agent-verifiable acceptance criteria now pass. No commit, push, or deployment was performed.

    Security retest

    • Re-ran the former exploit in production-like portal mode against an existing admin: Authorization: Bearer fabricated-token plus raw x-user-id: victim-admin now returns HTTP 401 JSON {"error":"Unauthorized"}. Reproduction command exit 0.
    • A fabricated bearer plus raw x-user-id also fails on another protected API route.
    • A valid legacy bearer plus a mismatched raw x-user-id resolves to the bearer session's actual user, not the caller-supplied identity.
    • Signed portal-broker identity still requires the shared secret and resolves an existing enabled local user.
    • Normalized-email lookup now follows LastEvaluatedKey across every Scan page. Focused tests prove both a unique match on a later page and duplicate matches split across pages; the latter reaches the callback as ambiguous and fails closed.

    Command evidence

    • NODE_ENV=test SKIP_AUTH=true node --import tsx --test tests/browser-auth.test.ts tests/portal-auth.test.ts tests/users.test.ts (from backend/) — exit 0; 32 passed, 0 failed.
    • npm --prefix backend test — exit 0; 1,195 passed, 0 failed, 173 suites.
    • npm --prefix backend run typecheck — exit 0.
    • npm --prefix backend run build — exit 0.
    • npm --prefix backend run test:e2e — exit 0; 342 Playwright tests passed, 0 failed.
    • uv run --with pytest python -m pytest tests/infra/test_template.py — exit 0; 19 passed, 0 failed.
    • make sam-validate — exit 0; infra/template.full.yaml is a valid SAM template.
    • make sam-build — exit 0; BackendFunction build succeeded.
    • npx tsx .tmp/tester-119/capture-auth-evidence.ts — exit 0; signed-out root 302 to /login, signed-out API JSON 401, missing-state callback safe 400, authenticated workspace 200, mapped /api/me 200.
    • git diff --check — exit 0.

    Screenshot evidence

    • .tmp/screenshots/issue-119/signed-out-auth-error.png — 1440x900, SHA-256 5e74d7ffe504e4a5cd755e8afb67d9de8606aff440783fb75937a90c795765b7.
    • .tmp/screenshots/issue-119/authenticated-workspace.png — 1440x900, SHA-256 6f9e1d3424ba2e107311b95e29793a450f2a33e343850a2e123819aaf2f743be.

    Both screenshots were regenerated and read after the fixes. The signed-out state is the safe local sign-in error with a retry link. The authenticated state is the DataOps Tasks / Work Queue empty state. Neither shows a 404, error page, broken layout, overlap, authorization code, token, session identifier, or credential.

    Acceptance criteria

    1. Authorization parameters, unpredictable state/nonce, S256 PKCE, short-lived server-side transaction: pass.
    2. Missing/expired/replayed/mismatched state and provider/token failures: pass.
    3. RS256/JWKS, issuer/audience/time/nonce/token-use/email verification and key refresh: pass.
    4. Exact enabled local-user mapping, case normalization, unknown/disabled/ambiguous denial, and multi-page uniqueness: pass.
    5. Opaque bounded server session, secure cookie, expiry/revocation, legacy bearer compatibility: pass.
    6. Authenticated portal/API access and signed-out page/API separation: pass.
    7. Local and Cognito logout plus reuse denial: pass.
    8. Production Basic/password form and legacy portal login removal: pass.
    9. Email intake, bookkeeping layered credentials, importers, portal broker, and legacy bearer isolation: pass.
    10. Non-secret SAM auth configuration and least-privilege contract: pass.
    11. GitHub Actions OIDC validation/build/deploy wiring and shared-stack non-mutation: pass.
    12. Required automated auth coverage: pass.
    13. Machine and bearer regression boundaries, including raw-header spoof rejection: pass.
    14. Tester command and visual evidence criterion: pass; checkbox updated.
      15-16. Real hosted password and Google flows remain [HUMAN], were not attempted by Tester, and should keep the issue open with the human label after shipping.

    READY FOR PM ACCEPTANCE

  8. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    REJECTED

    The core Cognito integration, local identity mapping, session model, machine-credential compatibility, and GitHub Actions OIDC deployment contract are strong, and Tester supplied the required full-suite and screenshot evidence. PM acceptance is still blocked by two security/product-flow gaps in the relying-party journey.

    1. Post-login return target is not guaranteed to stay on DataOps

    safeReturnTo accepts any value beginning with one slash except //. It therefore accepts a decoded value such as /\evil.example. Browsers apply URL backslash normalization for HTTPS URLs: new URL('/\\evil.example', 'https://ops.dtcdev.click').href resolves to https://evil.example/. After a successful callback, the implementation copies this value directly into the Location header.

    This violates the test scenario requiring callback completion to redirect to a clean same-origin page and creates a post-authentication open redirect.

    Required change:

    • Canonicalize and validate return_to against the DataOps callback/origin, not only string prefixes.
    • Reject backslashes, control characters, protocol-relative forms, auth endpoints, and any candidate whose parsed origin differs from https://ops.dtcdev.click; fall back to /.
    • Add regressions covering decoded backslash forms (including /\evil.example), //evil.example, absolute URLs, and valid same-origin paths.

    2. Failed callbacks leave the authorization code and state in the browser URL

    When state/binding/token exchange/token validation/account mapping fails, handleCallback returns the HTML error response directly from /auth/callback?code=...&state=.... The generic body does not print those values, but the completed error page remains on a URL containing them, and the error response lacks a Referrer-Policy: no-referrer header. Clicking Try again can consequently carry the callback URL as a same-origin referrer.

    This does not meet the unchecked acceptance criterion requiring authorization codes and session-sensitive values to be absent from URLs after callback completion.

    Required change:

    • Complete callback failures by redirecting to a clean same-origin error route without OAuth query data (or an equivalent mechanism that reliably removes it before rendering).
    • Apply Cache-Control: no-store and Referrer-Policy: no-referrer to the clean error journey.
    • Keep provider details and account-existence information generic.
    • Add a regression starting with a valid transaction and a failed token exchange/validation, asserting that the rendered/final error URL contains no code, state, token, verifier, or session value.

    User-visible error state

    The regenerated signed-out-auth-error.png is functional, but it is raw browser-default HTML on a blank page and visually reads like an unstyled server failure rather than part of DataOps. The authentication error state is explicitly within this issue's scope and the PM gate requires usable, coherent error states. Render a minimal DataOps-consistent error panel with clear retry action and accessible focus/heading treatment; do not expose technical provider details.

    Retest required

    After the fixes, Tester must independently rerun the affected auth/security tests plus the issue's full required commands, regenerate and inspect the signed-out error screenshot, and reconfirm the authenticated workspace screenshot. The two [HUMAN] hosted password/Google checks remain correctly deferred and should not block agent acceptance after these changes.

    I unchecked the affected ID-token/callback URL privacy criterion. I did not change code, commit, push, deploy, or add the human label.

  9. 18 remaining items

  10. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    ON-CALL DEPLOYMENT PASS — cookie-bootstrap hotfix e6d3b25

    The hotfix is deployed and the agent-verifiable production checks pass.

    CI/CD evidence

    Exact deployed artifact

    Lambda package last modified 2026-07-14T11:03:57Z, code SHA-256 mJ5Cyi3xZ/gRzsIy/u//93kGZhXB6lqYlc22O324wU0=.

    Read-only inspection of that package confirms:

    • packaged HTML has the dataops-auth-mode marker;
    • the portal router renders it as browser-cookie (standalone remains legacy-bearer);
    • browser-cookie bootstrap clears stale legacy/local bearer state, calls same-origin /api/me, and starts the workspace from the mapped backend user;
    • a failed/expired cookie routes to /login, sign-out routes to /logout, and API requests do not attach a legacy Authorization header;
    • the retired local password form cannot be rendered in browser-cookie mode.

    Live public probes

    • / → 302 /login through CloudFront.
    • /login → shared https://auth.dtcdev.click/oauth2/authorize with the expected client, exact callback, authorization-code flow, openid email profile, PKCE S256, fresh state/nonce/challenge, and dataops_oauth_tx cookie with HttpOnly; Secure; SameSite=Lax.
    • Following the redirect reaches the hosted login with HTTP 200.
    • /api/me without a session → JSON 401 {"error":"Unauthorized"} and no Basic challenge.
    • Invalid callback → 303 /auth/error, empty body, no query leakage, no-store, no-referrer, and expired transaction cookie; following it ends at the branded 403 error page with a safe retry action.
    • /logout → the configured Cognito logout URL with exact client/logout URI, expires dataops_session, and uses no-store.
    • auth.dtcdev.click TLS certificate/SAN is valid for the custom domain (current validity through 2027-01-27).
    • Lambda environment matches auth base URL, client ID, callback URL and logout URL expected by the deployed stack.

    No production session was created or harvested during on-call verification. The new production-portal Playwright coverage proves the valid opaque backend-session bootstrap contract; one final real-account browser confirmation remains appropriately human-owned under this issue's human label.

  11. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    LIVE PASSWORD CHECK PASS (2026-07-14T11:08:03Z UTC). A temporary administrator-created @datatalks.club Cognito identity and matching enabled DataOps user completed the real hosted password form in headless Chromium, returned through the production callback, rendered the DataOps workspace with the mapped Auth E2E user and Sign out, and returned 200 from /api/me. The session cookie was HttpOnly, Secure, and SameSite=Lax. GET /logout returned the registered Cognito logout redirect; replaying the prior opaque session returned JSON 401. The Cognito and DynamoDB test records were deleted. No credential, authorization code, token, verifier, cookie value, or exact disposable address was recorded. Screenshot inspected locally at 1440x900; SHA-256: ab4d56f5a21e54923dacff4667323e43f3bfed0acee9eb4cc080bbf8f158379b.

  12. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    GOOGLE INFRASTRUCTURE CHECK PASS: the deployed Cognito Google IdP is enabled for the DataOps client, the hosted UI exposes Google, and the production authorization endpoint hands off to accounts.google.com with the exact Cognito idpresponse URI. The deployed domain-guard Lambda independently accepts a synthetic member@datatalks.club federated event and rejects outsider@example.com. This is infrastructure evidence only; the remaining checked-off requirement still needs real Google accounts and no account credentials should be posted here.

  13. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    FINAL CROSS-SERVICE AUTH EVIDENCE

    • Live shared-Cognito password login, application session, and logout passed for Dapier, DataOps, Studio, Gym, and Datamailer.
    • Session cookies were verified Secure and HttpOnly; Datamailer also explicitly sets SameSite=Lax.
    • All five Cognito app clients are configured with both COGNITO and Google identity providers.
    • Dapier CI/CD run 29341247531 passed tests, SAM build, OIDC deployment, and health checks. Callback failures now return 303 /auth/error with no-store and no authorization code/state leakage.
    • Studio deploy run 29341513402 passed after secure proxy-session fix 8dc5f3f.
    • Datamailer CI/deploy run 29341779776 passed after Secure-cookie fix 9252b88.
    • Cleanup is complete: temporary Cognito user count is zero and temporary Datamailer Django users were removed.

    No real Google sign-in E2E is claimed here. The remaining [HUMAN] checks stay open: confirm an allowed @datatalks.club Google account succeeds and a non-domain Google account is rejected.

  14. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    PM spec correction: updated the stale Dependencies statement to the current audited state. Google federation is enabled, and all five shared-auth clients support both COGNITO and Google. The unchecked [HUMAN] real in-domain/out-of-domain Google allow/deny criterion and the human label remain unchanged. No code or deployment state was changed.

  15. alexeygrigorev commented on Jul 31, 2026

    @alexeygrigorev
    MemberAuthor

    PM TRACKER RECONCILIATION

    The status now records that the agent scope is shipped and deployed. The checked criteria remain supported by the final cookie-bootstrap Tester PASS, PM acceptance, deployed hotfix e6d3b25, successful On-Call checks, and the completed live hosted-password criterion.

    The human label remains. The real-Google criterion stays unchanged and unchecked: no real @datatalks.club Google success or non-domain Google rejection is claimed. Resume only when a human can verify both account classes and record sanitized results without credentials or tokens.

    Evidence: Tester pass, PM acceptance, On-Call deployment, live password check, final cross-service evidence.

  16. alexeygrigorev commented on Aug 13, 2026

    @alexeygrigorev
    MemberAuthor

    PM authentication lifecycle reconciliation complete.

    Decision: #119 stays open and blocked only on the two-account HUMAN Google check. No agent-verifiable implementation remains.

    Read-only findings:

    • Original integration fbcbc1a / e562000, CI-scoping hotfix 393a738 / 2dc413a, and cookie-bootstrap hotfix 15643d9 / e6d3b25 are all ancestors of current source and origin/main.
    • Tester and PM accepted the source and both hotfixes. On-Call deployed the final hotfix and verified the live Cognito/PKCE, callback-error, API denial, logout, TLS, and cookie-bootstrap seams.
    • A real hosted-password login, exact local User mapping, secure cookie, logout, and session-reuse denial already passed.
    • Google is enabled for the DataOps client and the deployed domain guard passed synthetic in-domain/out-of-domain probes, but that is deliberately not substituted for two real Google accounts.
    • Current canonical backend still owns this contract in backend/src/auth/browserAuth.ts, portal middleware/router, session/User repositories, SAM configuration, and the OIDC deployment workflow.
    • The remaining HUMAN gate is: a real @datatalks.club account is admitted and mapped to its existing enabled DataOps User; a real out-of-domain Google account is rejected and receives no DataOps session. Evidence must not include identities, credentials, codes, tokens, cookies, headers, or private provider payloads.

    #166 sequencing is now explicit. #119 was already published and deployed and must not add to or alter the reviewed A/B/C/D artifact. The human check may run against the current steady-state portal before transition begins; if any #166 phase starts first, wait until Phase D evidence and accepted cleanup restore steady-state deployment. #166 does not reopen #119 source work.

    I restored the missing human label. On a HUMAN pass, PM may close #119 without any source commit or deployment. On a failure, leave it open and file a separate bounded auth bug from sanitized evidence.

    No repository file, test, AWS/provider action, credential, User/session/data mutation, import/export/restore path, deployment, commit, or push was touched.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1ImportantbackendBackend/APIenhancementNew or improved functionalityfrontendFrontend UIhumanCode done or issue blocked on human verificationinfraDeployment and infrastructureportalShared portal shell and UXtestingTests and QA

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions