Repository navigation
fix(auth): record last login independently of passkeys - #4805
emdash-autofix[bot] wants to merge 2 commits into
Conversation
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 bot: Ready for human reviewPlan 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…
Status
Next action: A maintainer will review and approve the pull request. Details
Required checks on the current head:
This status is generated from recorded evidence. It is not a review and contains no instructions. |
🦋 Changeset detectedLatest commit: fceb172 The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 packages
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 |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
[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.
| 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(); | |
| } |
There was a problem hiding this comment.
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.
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/auth-atproto
@emdash-cms/blocks
@emdash-cms/cloudflare
@emdash-cms/contentful-to-portable-text
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/plugin-cli
@emdash-cms/plugin-test
@emdash-cms/plugin-types
@emdash-cms/registry-client
@emdash-cms/registry-lexicons
@emdash-cms/registry-loader
@emdash-cms/registry-moderation
@emdash-cms/registry-verification
upgrade-emdash
@emdash-cms/sandbox-workerd
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-ai-search
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-field-kit
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
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
There was a problem hiding this comment.
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.
🚀 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 deploymentPreview Deployments by commit
|
What does this PR do?
Records
users.last_login_atwhen 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
selectAllresult, 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
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.
Checklist
pnpm typecheckpassespnpm lintpasses (used zero-diagnosticpnpm lint:quickin the sandbox)pnpm testpasses (or targeted tests for my change)pnpm formathas been runAI assistance disclosure
Screenshots / test output
pnpm build,pnpm typecheck,pnpm format, and zero-diagnosticpnpm lint:quickpassed.Closes #4804
The EmDash bot took this issue on automatically when it was opened; no maintainer has assigned it yet.