Skip to content

Prefix changes are safe + engine views survive custom mount names - #13

Merged
rameerez merged 1 commit into
mainfrom
fix/prefix-warning-and-custom-mounts
Jul 27, 2026
Merged

Prefix changes are safe + engine views survive custom mount names#13
rameerez merged 1 commit into
mainfrom
fix/prefix-warning-and-custom-mounts

Conversation

@rameerez

Copy link
Copy Markdown
Owner

Two fixes, both hit for real while wiring api_keys into vehiclesdb.com:

1. The token_prefix warning was false

The initializer template said changing the prefix makes existing keys "fail authentication". It doesn't, on either strategy:

  • sha256 (default): lookup is by pure token digest — the prefix is never consulted.
  • bcrypt: lookup scopes by each key's own stored prefix column, with the known-prefixes fallback scan (already covered by authenticates with bcrypt when configured prefix mismatch triggers known prefixes scan).

Observed live: after switching an app from ak_ to a branded vdb_ prefix, previously-minted ak_ keys kept authenticating in production. The warning is now an accurate explanation of the actual behavior (including the one real cost: the slower known-prefixes path under bcrypt), and two new regression tests pin the guarantee per strategy — so users can brand their prefixes without fear of stranding integrations.

2. Custom mount names broke every engine view

mount ApiKeys::Engine => "/settings/api-keys", as: :settings_api_keys
# => undefined local variable or method 'api_keys' (in keys#index)

Six view call sites used the host-side routes proxy by its default name. Engine views now use bare engine-relative helpers (keys_path), which resolve against the engine's own routes under any mount name. A source-lint test keeps proxy calls out of the views (the suite deliberately runs without booting the dummy app, so a rendered test isn't possible in-harness); the dummy app gains a second custom-named mount for manual verification.

Full suite: 219 runs, 0 failures.

🤖 Generated with Claude Code

…e custom mount names

Two fixes proven in production on vehiclesdb.com:

1. The initializer template warned "Once set, do NOT change [token_prefix]
   or existing keys will fail authentication!" — false on both strategies.
   sha256 looks up by pure token digest (prefix never consulted); bcrypt
   scopes by each key's OWN stored prefix via the known-prefixes scan
   (which the suite already exercised). Observed live: keys minted under
   ak_ kept authenticating after the app rebranded to vdb_. The warning
   becomes an accurate explanation, and two regression tests pin the
   guarantee per strategy.

2. Engine views called the host-side routes proxy by its default name
   (api_keys.keys_path), so mounting with a custom `as:` exploded with
   "undefined local variable 'api_keys'". Engine views now use their own
   engine-relative helpers, which resolve under any mount name. A source
   lint test keeps the proxy out of views; the dummy app gains a second,
   custom-named mount for manual verification.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@claude

claude Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review

Both fixes are well-motivated and the sha256/bcrypt regression tests for token_prefix in test/services/authenticator_test.rb accurately reflect Authenticator#find_and_verify_key — sha256 truly ignores the prefix at lookup time, and bcrypt's known-prefixes fallback (fetch_known_prefixes) does scope by each key's own stored prefix. The updated initializer comment is accurate, including the "one real cost" callout about the slower known-prefixes path. Good tests, good docs fix. ApiKeys.reset_configuration! runs in ApiKeys::Test#setup, so mutating config.token_prefix in the new tests does not leak state across tests — no cleanup bug there.

Bug: the custom-mount fix is incomplete — the shared layout still calls the host-side proxy

app/views/layouts/api_keys/application.html.erb:171 still has:

<%= link_to "My API Keys", api_keys.keys_path, class: "active" %>

This is the layout rendered for every engine page (no controller sets layout explicitly, so Rails resolves it via the isolated-engine view-path convention). Under a custom mount name (mount ApiKeys::Engine => "...", as: :settings_api_keys), this line raises the exact undefined local variable or method 'api_keys' error the PR sets out to fix — on every engine page, since the layout wraps them all. This means the bug reported in the PR description (discovered live on vehiclesdb.com) is not actually fully fixed by this PR.

The reason it slipped through: test/views_route_helpers_test.rb's glob is scoped to app/views/api_keys/**/*.erb, which excludes app/views/layouts/api_keys/**/*.erb — the one directory where the bug still lives. Widening the glob (e.g. restricting app/views/**/*.erb to files that render inside the engine, or explicitly adding the layouts path) would have caught this. Suggest fixing the layout link to keys_path and broadening VIEWS_GLOB to include app/views/layouts/api_keys/.

Minor / non-blocking

  • fetch_known_prefixes caches the distinct prefix list for cache_ttl seconds (pre-existing, not introduced here). With token_prefix changed dynamically and a nonzero cache TTL, there's a theoretical window right after a prefix rotation where a stale cached prefix list could momentarily miss a scan — but that's unrelated to what this PR's tests guarantee (existing keys under retired prefixes), so it does not contradict the PR's claims. Flagging as pre-existing, not something to fix here.
  • The source-lint test approach (regex over .erb files) is a reasonable stand-in given the suite deliberately does not boot the dummy app, but as shown above its coverage gap (missing layouts/) is exactly what let a real offender through — worth a second glance at whether other non-api_keys/-prefixed view dirs (e.g. mailer views, if any are added later) should be swept too.

Overall: solid, well-tested fix for issue #1 (the token_prefix warning). Issue #2's fix (custom mount names) is on the right track but incomplete — the layout needs the same treatment before this can be considered fully resolved.

@rameerez
rameerez merged commit 634f523 into main Jul 27, 2026
7 checks passed
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