Skip to content

fix(login): say so when the install has no email provider (#3655) - #3756

Merged
vybe merged 1 commit into
devfrom
fix/3655-login-no-email-provider
Oct 17, 2026
Merged

vybe merged 1 commit into
devfrom
fix/3655-login-no-email-provider

Conversation

@dolho

@dolho dolho commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • With no email provider configured, which is the default on a localhost install that skipped the Resend key, the sender falls back to console mode: the code goes to the backend log and the send reports success. The Login page said "We sent a 6-digit code", and the person waited for an email that never came.
  • GET /api/auth/mode now carries email_delivery_available. It's an install-level flag, like ent#849's email_code_login_enabled, computed by EmailService.can_deliver():
    • false for console, an unknown provider, resend without a key (Settings or env), smtp without host/user/password, or sendgrid without a key;
    • true if the config can't be read, so the page then behaves exactly as today.
  • When it's false, step 2 says instead:
    • no email was sent;
    • if the address may sign in here, its code is in the backend log, readable with docker compose logs backend;
    • an admin can use Admin Login (there's a button) and add a provider under Settings → Integrations.
  • The "if" wording is deliberate: POST /api/auth/email/request answers the same for every address (SEC: User and Agent Enumeration via differential API responses #186), so the page can't claim a code exists. That endpoint never consults the flag (pinned by an AST test). The code itself is never rendered.
  • New FAQ entry: "I requested a login code on localhost and nothing arrived — am I locked out?"

Changes

  • src/backend/services/email_service.py: can_deliver().
  • src/backend/routers/auth.py: email_delivery_available on /api/auth/mode.
  • src/frontend/src/stores/auth.js: emailDeliveryAvailable (null or an older backend reads as "delivers").
  • src/frontend/src/views/Login.vue: the notice, using status-warning tokens and BaseButton, and a useAdminLogin handler.
  • Tests:
    • tests/unit/test_3655_email_delivery_flag.py: providers and keys, unreadable config, the mode flag, and the request handler never reading it. 8 fail and 2 error without the fix.
    • loginNoEmailProvider.spec.js: the copy, no six-digit number rendered, the Admin Login button, unchanged behaviour when email is delivered, and the store reading the flag.
  • docs/user-docs/faq/getting-started.md.

Tested against a local instance

Provider console, no Resend key. This branch's four files were hot-swapped into the running stack and restored afterwards. A signed-out browser requested a code for a non-whitelisted address:

  • Before: "📧 We sent a 6-digit code to nobody-3655@example.com".
  • After: /api/auth/mode returns email_delivery_available: false, and the notice shows in both light and dark.

Screenshots were shared with the reviewer separately (not committed).

Test Plan

  • test_3655_email_delivery_flag.py + test_ent849_email_code_policy.py + test_verification_email.py: 58 passed.
  • Login specs + rawColorRatchet.spec.js: 29 passed. The colour ratchet holds: no raw palette classes added.
  • With a Resend key saved in Settings, the page reads "We sent a 6-digit code" as before.

Fixes #3655

🤖 Generated with Claude Code

With no email provider configured (the default for a localhost install
that skipped the Resend key) the sender falls back to console mode: the
code goes to the backend log and the send reports success. The Login
page still said "We sent a 6-digit code", and the person waited for an
email that never came.

GET /api/auth/mode now carries email_delivery_available, an install-level
flag like email_code_login_enabled, from EmailService.can_deliver(): false
for console, an unknown provider, or a provider missing its key or SMTP
settings; true on an unreadable config, so the page behaves as today.
When it is false, step 2 says no email was sent, that a code for an
address allowed to sign in is in the backend log (docker compose logs
backend), and points at Admin Login and Settings -> Integrations. The
wording is conditional because the request endpoint answers the same for
every address (#186) and never consults the flag. The code itself is
never rendered. FAQ entry added.

Fixes #3655

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dolho dolho added the ui PR touches the frontend UI — triggers Playwright e2e tests label Oct 15, 2026

@vybe vybe 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.

merge-train: batch validated on train PR #3781 (all Tier 1 + Tier 2 checks green).

@vybe
vybe merged commit 5f3b47a into dev Oct 17, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ui PR touches the frontend UI — triggers Playwright e2e tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants