Skip to content

fix(auth): honor typed passkey credential config#17

Merged
intel352 merged 16 commits into
mainfrom
feat/admin-bootstrap-passkey-upgrade
May 18, 2026
Merged

fix(auth): honor typed passkey credential config#17
intel352 merged 16 commits into
mainfrom
feat/admin-bootstrap-passkey-upgrade

Conversation

@intel352
Copy link
Copy Markdown
Contributor

@intel352 intel352 commented May 18, 2026

Summary

  • accept strict-proto snake_case auth.credential config keys (rp_display_name/rp_id) while preserving legacy camel-case inputs
  • derive RP ID from origin before optional module unregisters, so origin-only optional passkey config registers correctly
  • prepare workflow-plugin-auth v0.2.5 release metadata so GoReleaser's manifest-version gate and release download URLs match the tag

Dependencies

Verification

  • PLUGIN_MANIFEST_EXPECT_VERSION=0.2.5 GOWORK=off go test ./internal -run TestIntegration_PluginManifestAndStepTypes -count=1
  • GOWORK=off go test ./internal

intel352 and others added 16 commits May 17, 2026 21:52
3-phase plan: BMW 500 hotfix, plugin v0.3.0 admin bootstrap steps,
BMW migration onto plugin auth. SSO IDP deferred to Phase II.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Resolves 4 Critical + 8 Important findings. Adopts magic-link reuse
(no new step types/migration/HKDF/module type); broadens Phase 1 to
exhaustive nil-deref audit covering signup; defers generate_token
retirement; resolves all rev-1 hedges in-design.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Drops all plugin work (YAGNI). All 4 PRs land in buymywishlist:
- PR-0: BMW engine bump v0.20.1 -> v0.51.6 (likely real 500 source)
- PR-1: exhaustive nil-deref hotfix
- PR-2: admin bootstrap pipelines + magic_link_tokens reuse + super_admin SQL seed
- PR-3: bespoke -> plugin password step rename (keep generate_token)

Phase II = plugin extraction when 2nd consumer arrives.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Cycle 3 surfaced 5 Critical findings, mostly fact errors in rev 3.
Skill cap (2 revisions) reached; per autonomous-mode mandate, applying
mechanical fixes without re-running cycle 4 adversarial review:

- bcrypt cost regression: drop hash_password swap (plugin DefaultCost=10
  vs bespoke=12); keep bespoke. Phase II opens plugin v0.2.5 with
  configurable cost.
- magic_link_generate ignores expiry_minutes (hardcoded 15): use 15 min.
- Role schema corrected to actual ('user'/'admin'/'super_admin'/
  'moderator'/'support').
- generate_token call-site count corrected (9 not 10).
- Timing oracle accepted with note (operator-only endpoint).
- Bootstrap-redeem switched POST + token-hash binding.
- Existing magic-link queries gain purpose='login' filter.
- Phase II 1-page interface sketch added per user soft ask.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Per plan-phase cycle 3 adversarial review: URL returned in response
body (not log), redeem endpoint GET (browser-clickable, matching
existing /auth/magic-link pattern). Trade-off documented.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
BMW http.server binds :8080 all-interfaces; Tailscale exposes :443.
Bearer token is sole protection. Strengthened via min-entropy (>=32)
and rate-limit rules in plan rev 6.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…te counts

Both bespoke steps have 2 call sites each (not 1). hash_password: :881 + :11116;
verify_password: :1073 + :9447.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ming-oracle row

Rev 6 amendment claimed 'dropped throughout' but missed 4 places:
Goal §2, Top Doubts timing row, Top Doubts allowlist row, Assumption #2.
All updated to 'bearer-token-gated' or 'all-interfaces publicly reachable'.

Bootstrap-link response shape reconciled: rev 5 plan moved URL to body
(URL on hit / 404 on miss) — design row updated to match instead of
claiming 'constant 200 timing-equalisation'.

Rate-limit limitation documented (engine no-op per modules.go:139-174).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Rev-8 amendment header claimed scrub-throughout but missed:
- §3 'configured to bind localhost-only via existing BMW ingress'
- §3 allowlist branches 'SAME response' (now 200/404 split)
- §3 URL 'embedded in operator-facing log entry' (now in body)
- §4 'POST /admin/bootstrap-redeem' + 'Body: {token}' (now GET +
  query params per rev-5 amendment)
- §6 'curl --unix-socket /var/run/bmw.sock' (now TCP)

All three sections rewritten to reflect actual plan rev 11
implementation: publicly-reachable, GET redeem, NEW enrol-passkey
pipelines (additive), 24h JWT expiry in config block, SQL-side
expires_at filter, case-insensitive email.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…g GET handler

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…correct v0.51.6 → v0.51.2 pin

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Cycle-14 deferred PR-3 in the header amendment but the body still had
a full active PR-3 section + verification gate + rollback row +
sequencing row + file touch row. Cycle-15 caught the drift. All
updated to reflect Phase II deferral.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…s, References, Assumption #5)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Accept snake_case strict-proto credential config and derive RP ID before optional modules decide whether to unregister.
Updates release manifest URLs so the v0.2.5 tag can pass GoReleaser's manifest-version gate and publish installable artifacts.
@intel352 intel352 merged commit 278d504 into main May 18, 2026
6 checks passed
@intel352 intel352 deleted the feat/admin-bootstrap-passkey-upgrade branch May 18, 2026 13:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant