# Bilko — Chrome-specific login failure (Brave OK) — browser-compat troubleshooting — MC #8919

# Bilko — Chrome-specific login failure (Brave OK) — browser-compat troubleshooting

**MC #8919** | Priority L | Category frontend | Last verified 2026-08-08

## Symptom

A real Chrome browser cannot complete login on the Bilko web app, while Brave
(also Chromium-based) logs in fine on the same machine, and an automated
Playwright/Chromium run also logs in fine.

Because Playwright's clean Chromium profile succeeds, the failure is **not**
in Bilko's server-side code, CSP, or headers — it is local to the affected
Chrome **profile** (extensions, cached state, cookies, or autofill
extensions racing the login form). This runbook gives support staff a fast
path to confirm that and fix the local profile, plus a script to re-verify
the server side in seconds if this ever needs re-checking.

## Current architecture (verified live 2026-08-08)

Bilko login is not a simple username/password POST — it goes through
**Microsoft Entra External ID (CIAM)**. The web app CSP must allow the CIAM
domains for the login iframe/redirect to work at all:

- `connect-src` / `frame-src` on the web app allow `https://*.ciamlogin.com`
  and `https://login.microsoftonline.com`.
- Web app: `https://app.bilko.io` (also `app.bilko.cloud`, `app.bilko.company`)
  → Cloudflare Worker → Azure Container Apps `bilko-web-demo`.
- API: `https://api.bilko.io` (also `api.bilko.cloud`, `api.bilko.company`)
  → Cloudflare Worker → Azure Container Apps `bilko-api-demo`.
- CSP source of truth: `apps/api/src/main/kotlin/no/alai/bilko/plugins/SecurityHeaders.kt`
  (api CSP — plain REST default) and the Next.js web app's own CSP header
  (app shell — CIAM/Sentry/fonts allowlist, see script output below for the
  live value).

**Note on the original task's URL:** the task was filed against
`bilko-demo.alai.no`. That domain is **retired** — it was dropped from the
post-deploy smoke checks in MC #105368 ("dropped dead bilko-demo.alai.no
domain check", see `DEPLOY-MAP.md`). The live demo entry points are
`app.bilko.io` / `app.bilko.cloud` / `app.bilko.company`. If a user reports
this again, confirm which host they actually used first.

**Note on the "raw run.app URL" long-term fix mentioned in the original
task:** this is already done. The web app is served from a custom domain
(`app.bilko.io` etc.) via a Cloudflare Worker, not a raw
`*.azurecontainerapps.io` URL — so the "ad-blockers flag *.run.app/*.azurecontainerapps.io"
concern this task raised no longer applies to the primary login path.

## Step 1 — Rule out the server side (30 seconds)

```bash
bash ~/system/tools/bilko-header-debug.sh app.bilko.io api.bilko.io
```

This fetches live headers from both hosts and checks that the web app's CSP
still allowlists the CIAM domains (if it doesn't, that's a real CSP bug —
stop here and file a build task). As of 2026-08-08 this passes: both CIAM
domains are present in `connect-src`, and `frame-src` allows
`https://*.ciamlogin.com`.

If this script passes, the problem is downstream of the server — i.e. it's
the affected Chrome profile, per Step 2.

## Step 2 — Browser-side checklist (Chrome-specific)

Work through these in order; each takes under a minute:

1. **Clean profile test.** Open Chrome in a fresh/guest profile (or
   `chrome --user-data-dir=/tmp/chrome-clean-test`) and try login there. If
   it works, the problem is 100% the original profile's extensions/state —
   skip to step 3. If it still fails in a clean profile, this is no longer a
   "local issue" — escalate and re-open this task as a real bug.
2. **DevTools Network tab**, filter by `ciamlogin` and `microsoftonline`.
   Look for requests with status `(failed) net::ERR_BLOCKED_BY_CLIENT` —
   that status specifically means a browser extension blocked the request,
   not a server/CSP problem.
3. **DevTools Console**, look for `Refused to connect/frame ... because it
   violates the following Content Security Policy directive`. If you see
   this, capture the exact directive and re-run Step 1 — the live CSP may
   have regressed.
4. **Known culprits to disable one at a time** (re-test login after each):
   - **uBlock Origin** — filter lists such as EasyPrivacy commonly block
     `*.microsoftonline.com` / `*.ciamlogin.com` as "MS telemetry". Toggle
     the extension off for the site, or check the blocked-request log
     (uBlock icon → the red counter → look for `ciamlogin.com` /
     `microsoftonline.com` entries).
   - **Privacy Badger** — blocks third-party cookies from domains it deems
     trackers after repeated visits; CIAM's cross-domain auth flow relies on
     a third-party cookie/redirect round-trip. Check its site report for
     "blocked" entries on the CIAM domains.
   - **1Password / other password-manager autofill** — can race the login
     form's React state (autofill writes to the DOM input directly, bypassing
     React's controlled-input `onChange`, so the form's internal state never
     updates and submit sends empty/stale values). Test with the extension
     disabled, or manually re-type the last character in each field before
     submitting.
5. **Cookies for the CIAM domains.** Chrome's third-party cookie handling
   differs by version/flag state from Brave's shields. Check
   `chrome://settings/content/all` (or the padlock → cookies) for
   `ciamlogin.com` / `login.microsoftonline.com` and clear them, then retry.

## Step 3 — Fix and document

Once the culprit is identified (extension, stale cookie, or autofill race),
the fix is local to that Chrome profile — there is nothing to change in the
Bilko codebase for that specific case. Log the confirmed culprit here (append
below) so future occurrences can skip straight to the fix:

| Date | Confirmed culprit | Fix applied |
| ---- | ------------------ | ----------- |
| _(none confirmed yet — original 2026-04-23 report was never root-caused because Playwright already proved the server side clean; awaiting a reproduction with DevTools open)_ | | |

## Related

- MC #8919 (this task)
- MC #105368 — Promote_Demo smoke check cleanup, dropped dead `bilko-demo.alai.no` check
- MC #105793 — stage cross-host cookie fix (`Domain=.bilko.cloud`), same CIAM auth flow, different symptom (cookie not sent cross-host on stage, not extension-blocked)
- `docs/security/HEADERS-AUDIT.md` (Bilko repo) — full header audit, last updated 2026-03-06 (predates the CIAM CSP allowlist above; this runbook's Step 1 output is the current source of truth for the web app CSP)
- Debug tool: `~/system/tools/bilko-header-debug.sh`