Skip to content

fix(auth): record last login independently of passkeys - #4805

Open
emdash-autofix[bot] wants to merge 2 commits into
mainfrom
emdash-bot/issue-4804
Open

emdash-autofix[bot] wants to merge 2 commits into
mainfrom
emdash-bot/issue-4804

Conversation

@emdash-autofix

Copy link
Copy Markdown
Contributor

What does this PR do?

Records users.last_login_at when a production sign-in establishes a session, rather than deriving every login from passkey usage. Covers magic links, built-in OAuth, passkey login/registration, external-auth sessions and AT Protocol. Both Users list and account details prefer the recorded timestamp, falling back to existing passkey history for upgraded accounts.

Reproduced the reported symptom with a real magic-link login for an admin with zero passkeys: Last Login remained Never after refresh. The regression test failed before the fix and the same browser flow now shows a login date. Previously untracked logins become visible after signing in again following the upgrade; existing sessions are not backfilled with invented login times. Session refreshes and dev bypasses do not count as sign-ins.

Adds restart-safe migration 098. Existing standalone Kysely auth-adapter databases still work without the column: the adapter reads it from the existing selectAll result, never names it in a query, and preserves legacy passkey history. AT Protocol checks for the column before writing, so upgrading the provider ahead of core does not break login; that path adds one read per login. Both compatibility cases have real-database regression coverage.

No anonymous-request queries are added. Production session establishment adds one awaited write. External auth records at the existing session-id-change boundary: an unavailable session read is treated as a fresh session, while same-user requests do not update the timestamp. External auth without an Astro session driver does not record a session login. Database write failures use existing error handling rather than silently establishing an unrecorded session; a magic-link user may need a fresh link if its consumed token is followed by a database failure.

Type of change

  • Bug fix
  • Feature (requires an accepted feature plan or RFC)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Your connection to this change

Project-operated autofix bot handling an auto-intake bug report under the AI policy exemption. Human maintainer approval is required before merge.

  • I use EmDash and ran into the problem this PR addresses
  • I maintain a tool, dependency, or service that EmDash uses, and this change concerns that integration
  • A Maintainer approved this specific change before I opened the PR

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes (used zero-diagnostic pnpm lint:quick in the sandbox)
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes
  • User-visible strings in the admin UI are wrapped for translation (not applicable: no new UI strings)
  • I have added and reviewed the user-facing changeset
  • New features link to their merged design PR (not applicable)
  • I have included screenshots below

AI assistance disclosure

  • This PR includes AI-generated code or text — model/tool: cloudflare-ai-gateway:gpt-6.1-sol via the EmDash bot (Pi Durable)

Screenshots / test output

  • Root pnpm build, pnpm typecheck, pnpm format, and zero-diagnostic pnpm lint:quick passed.
  • Core auth, external-auth and migration tests: 35 files, 455 tests passed, including AT Protocol timestamp/missing-column and standalone-adapter compatibility tests.
  • Auth package: 8 files, 133 tests passed. AT Protocol package: 3 files, 27 tests passed.
  • PostgreSQL was not available locally; new database tests use the dialect wrapper for PostgreSQL-enabled runs.
  • A temporary Playwright Chromium spec performed real magic-link verification and refreshed Users before taking screenshots; removed afterwards.
Users before Users after
Users list: signed-in admin with zero passkeys shows Never Users list: the same admin has a recorded login date after sign-in and refresh
Account details before Account details after
Account Info shows Last login Never Account Info shows a recorded Last login date

Closes #4804

The EmDash bot took this issue on automatically when it was opened; no maintainer has assigned it yet.

Reproduced Never after successful magic-link sign-in and refresh. Record user-level login timestamps at production session establishment, preserve legacy passkey history and compatibility with older adapter/provider databases, and add migration 098 plus regression tests. Verified root build, typecheck, format, clean quick lint, 455 focused core tests, 133 auth tests, 27 AT Protocol tests, and Playwright before/after screenshots. Independent follow-up review found no remaining defects.

EmDash-Bot-Case: 1198246700-issue-6333648486
EmDash-Bot-Candidate: cand-1-e7485503ac
@emdash-autofix emdash-autofix Bot added the bot:fix Assign this issue or pull request to the EmDash bot to fix label Nov 11, 2026
@emdash-autofix

emdash-autofix Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor Author

EmDash bot: Ready for human review

Plan

The newly reported Tests failure is only the aggregate gate for the same two failures on the old commit: the external-auth fixture already repaired in submitted candidate 2, and the unrelated Cloudflare taxonomy flake. Candidate 2 is await…

  • Trace authentication and Users data and reproduce the missing timestamp
  • Implement a scoped fix with regression coverage and changeset
  • Run focused checks and independent review before publication
  • Repair the external-auth database fixture and verify CI failure classification

Status

  • Required checks passed, the review bot approved the current head, and the head merges cleanly.

Next action: A maintainer will review and approve the pull request.

Details
Candidate cand-2-a66f39faad (tree c8e747893a3b)
Published head fceb172357fe
Reviewer's latest review of head approved
Reporter confirmation not yet
Human approval required before merge

Required checks on the current head:

  • Typecheck: succeeded
  • Tests: succeeded
  • Smoke Tests: succeeded
  • Browser Tests: succeeded
  • Format: succeeded
  • CLAssistant: succeeded
  • Measure: succeeded

This status is generated from recorded evidence. It is not a review and contains no instructions.

@changeset-bot

changeset-bot Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: fceb172

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
emdash Patch
@emdash-cms/auth Patch
@emdash-cms/auth-atproto Patch
@emdash-cms/cloudflare Patch
@emdash-cms/plugin-test Patch
@emdash-cms/sandbox-workerd Patch
@emdash-cms/admin Patch
@emdash-cms/blocks Patch
create-emdash Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
@emdash-cms/plugin-embeds Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach is right. Last Login previously came from MAX(credentials.last_used_at), so magic-link, OAuth and external-auth users showed "Never". The PR adds a users.last_login_at column and writes it wherever a production sign-in starts a session. The Users list and detail read it first and fall back to passkey history. Both reviewers approved and found nothing that needs fixing. One reviewer checked session coverage, write ordering, migration registration, standalone-adapter and AT Protocol compatibility, tests and the changeset. The other reviewed statically and ran no tests. The only finding is a minor suggestion: migration 098's down should check columnExists before dropping the column, as sibling migrations do.


Reviewed by anthropic/claude-opus-5-5 and openai/gpt-6.1-sol.

}

export async function down(db: Kysely<unknown>): Promise<void> {
await db.schema.alterTable("users").dropColumn("last_login_at").execute();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] up checks columnExists before adding the column, but down drops last_login_at without a check. Sibling migrations (070_collection_routable, 076_collection_nav_group, 060_collection_admin_config) check in both directions, because D1 has no transactions and a partly applied rollback can be retried. Checking here too keeps down safe to re-run.

Suggested change
await db.schema.alterTable("users").dropColumn("last_login_at").execute();
if (await columnExists(db, "users", "last_login_at")) {
await db.schema.alterTable("users").dropColumn("last_login_at").execute();
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I’ll leave this unchanged: EmDash migrations are forward-only, and this follows migration 058’s unconditional down. The production upgrade path is restart-safe and covered by the retry test; rollback hardening isn’t needed for this fix.

@pkg-pr-new

pkg-pr-new Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@4805

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@4805

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@4805

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@4805

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@4805

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@4805

emdash

npm i https://pkg.pr.new/emdash@4805

create-emdash

npm i https://pkg.pr.new/create-emdash@4805

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@4805

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@4805

@emdash-cms/plugin-test

npm i https://pkg.pr.new/@emdash-cms/plugin-test@4805

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@4805

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@4805

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@4805

@emdash-cms/registry-loader

npm i https://pkg.pr.new/@emdash-cms/registry-loader@4805

@emdash-cms/registry-moderation

npm i https://pkg.pr.new/@emdash-cms/registry-moderation@4805

@emdash-cms/registry-verification

npm i https://pkg.pr.new/@emdash-cms/registry-verification@4805

upgrade-emdash

npm i https://pkg.pr.new/upgrade-emdash@4805

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@4805

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@4805

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@4805

@emdash-cms/plugin-ai-search

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-search@4805

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@4805

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@4805

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@4805

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@4805

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@4805

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@4805

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@4805

commit: fceb172

Repair the CI failure caused by the external-auth plugin-route test's empty database mock: use a migrated real database, retain authentication assertions, and verify last-login persistence. All 753 core Astro unit tests and 16 focused login/plugin tests pass; formatting, quick lint and diff checks pass. Investigated the separate Cloudflare taxonomy-checkbox failure and reproduced it as flaky locally (first attempt failed, retry passed); no taxonomy or dev-bypass behavior changed. Independent review found no blockers.

EmDash-Bot-Case: 1198246700-issue-6333648486
EmDash-Bot-Candidate: cand-2-a66f39faad

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach is right. Last Login previously came only from MAX(credentials.last_used_at), so magic-link, OAuth and external-auth users showed "Never". This PR adds a users.last_login_at column and writes it wherever a production sign-in starts a session. The Users list and detail read that column first and fall back to passkey history. Both reviewers rechecked session coverage, list/detail fallback, migration registration, standalone-adapter and AT Protocol compatibility, tests and the changeset, and found nothing new that needs fixing. The earlier suggestion to check columnExists in migration 098's down was reasonably declined, because migrations here are forward-only and migration 058 does the same. It is not raised again. One reviewer reviewed statically and did not run tests, builds or lint, and PostgreSQL/runtime execution remains unverified.


Reviewed by anthropic/claude-opus-5-5 and openai/gpt-6.1-sol.

@emdash-autofix emdash-autofix Bot added triage/awaiting-reporter Fix pushed, waiting on reporter to verify and removed triage/awaiting-reporter Fix pushed, waiting on reporter to verify labels Nov 11, 2026
@cloudflare-workers-and-pages

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://emdash-bot-issue-4804.try.emdashcms.com, https://emdash-bot-issue-4804-emdash-playground.emdash-cms.workers.dev (commit fceb172)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://96c807ca.try.emdashcms.com, https://96c807ca-emdash-playground.emdash-cms.workers.dev fceb172 2026-11-11T18:31:48.262Z Visit the dashboard ↗

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

Labels

area/auth area/core bot:fix Assign this issue or pull request to the EmDash bot to fix cla: signed size/L triage/awaiting-reporter Fix pushed, waiting on reporter to verify

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Users page shows "Last Login: Never" for the currently signed-in account, even after refreshing

0 participants