Skip to content

feat: add a cache challenge store - #152

Merged
RenzoMinelli merged 12 commits into
masterfrom
rm--cache-challenge-store
Oct 9, 2026
Merged

RenzoMinelli merged 12 commits into
masterfrom
rm--cache-challenge-store

Conversation

@RenzoMinelli

@RenzoMinelli RenzoMinelli commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

What: Adds ChallengeStores::Cache (:cache), which keeps WebAuthn challenges in Rails.cache instead of the session.

Why: Clients without a cookie session cannot use the session store. Support for those API clients follows in #153.

Decisions:

  • With no session, the store finds the challenge through the credential's clientDataJSON.
  • The store reads the credential from the request, so the feat: add configurable challenge store for WebAuthn challenges #150 interface does not change.
  • The cache key holds a SHA-256 digest of the challenge, so keys have a fixed length.
  • The cache must be shared by all servers. MemoryStore and FileStore are not.

How to test: bundle exec rspec spec/devise/webauthn/challenge_stores spec/requests/devise/cache_challenge_store_spec.rb. The request spec asks for the challenge and signs in from a different session.

RenzoMinelli added a commit that referenced this pull request Sep 25, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@RenzoMinelli
RenzoMinelli marked this pull request as draft September 25, 2026 19:04
RenzoMinelli added a commit that referenced this pull request Sep 25, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@RenzoMinelli
RenzoMinelli force-pushed the rm--cache-challenge-store branch from 87acce5 to 05d846d Compare September 25, 2026 19:08
RenzoMinelli added a commit that referenced this pull request Sep 25, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@RenzoMinelli
RenzoMinelli force-pushed the rm--cache-challenge-store branch from 05d846d to ffe1a99 Compare September 25, 2026 19:25
@RenzoMinelli
RenzoMinelli force-pushed the rm--cache-challenge-store branch from ffe1a99 to 404ae7c Compare September 25, 2026 20:20
RenzoMinelli added a commit that referenced this pull request Sep 25, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RenzoMinelli added a commit that referenced this pull request Sep 25, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@RenzoMinelli
RenzoMinelli force-pushed the rm--cache-challenge-store branch from 404ae7c to 9eb1ffd Compare September 25, 2026 20:34
@RenzoMinelli
RenzoMinelli added this pull request to stack #156 October 2, 2026 20:17
RenzoMinelli added a commit that referenced this pull request Oct 2, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@RenzoMinelli
RenzoMinelli force-pushed the rm--cache-challenge-store branch from 9eb1ffd to 6646a39 Compare October 2, 2026 22:06
RenzoMinelli added a commit that referenced this pull request Oct 6, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@RenzoMinelli
RenzoMinelli force-pushed the rm--cache-challenge-store branch from 6646a39 to f69e4d6 Compare October 6, 2026 12:23
RenzoMinelli added a commit that referenced this pull request Oct 7, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@RenzoMinelli
RenzoMinelli force-pushed the rm--cache-challenge-store branch from f69e4d6 to 8dd3657 Compare October 7, 2026 13:12
Base automatically changed from rm--challenge-store to master October 7, 2026 21:15
@RenzoMinelli
RenzoMinelli force-pushed the rm--cache-challenge-store branch from 8dd3657 to 424e8b8 Compare October 7, 2026 21:15
RenzoMinelli and others added 4 commits October 7, 2026 18:27
- `ChallengeStores::Cache` finds the challenge through the credential's
  clientDataJSON and consumes it once.
- Accept `public_key_credential` as a JSON object or string.
- Respect `skip_session_storage` (`:params_auth`) in both WebAuthn
  strategies.
- Load the options controllers under an `ActionController::API` parent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…request

Stores already receive the request, so callers no longer pass the credential and the store interface stays as merged in #150.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@RenzoMinelli
RenzoMinelli force-pushed the rm--cache-challenge-store branch from 424e8b8 to df689de Compare October 7, 2026 21:33
@RenzoMinelli
RenzoMinelli marked this pull request as ready for review October 7, 2026 21:49
Comment thread lib/devise/webauthn/challenge_stores/cache.rb Outdated
Comment thread spec/devise/webauthn/challenge_stores/cache_spec.rb Outdated
Comment thread spec/devise/webauthn/challenge_stores/cache_spec.rb Outdated
Comment thread lib/devise/strategies/webauthn_two_factor_authenticatable.rb Outdated
Comment thread lib/devise/webauthn/challenge_stores/cache.rb Outdated
Comment thread lib/devise/webauthn/challenge_stores/cache.rb
RenzoMinelli and others added 2 commits October 8, 2026 18:11
Also memoize the credential and its challenge, and simplify the spec setup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This PR only adds the cache challenge store. JSON object credentials,
`skip_session_storage` and the `ActionController::API` parent move to a
follow-up, together with the two-factor flow that still reads the session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@RenzoMinelli RenzoMinelli changed the title feat: add a cache challenge store for API clients without a session feat: add a cache challenge store Oct 8, 2026
RenzoMinelli and others added 2 commits October 8, 2026 18:19
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keeps keys a fixed length and keeps raw challenges out of the cache.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RenzoMinelli added a commit that referenced this pull request Oct 8, 2026
- Accept `public_key_credential` as a JSON object or string.
- Respect `skip_session_storage` (`:params_auth`) in both WebAuthn
  strategies.
- Load the options controllers under an `ActionController::API` parent.

Moved from #152, which now only adds the cache challenge store.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@santiagorodriguez96 santiagorodriguez96 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking great! 🔝

Just a couple of small comments!! 💪

Comment thread spec/requests/devise/cache_challenge_store_spec.rb Outdated
Comment thread lib/devise/webauthn/challenge_stores/cache.rb Outdated
Comment thread spec/devise/webauthn/challenge_stores/cache_spec.rb
Comment thread spec/devise/webauthn/challenge_stores/cache_spec.rb
Comment thread README.md
Comment on lines +142 to +143
Devise::Webauthn::ChallengeStores::Cache.cache = Rails.cache
Devise::Webauthn::ChallengeStores::Cache.expires_in = 5.minutes

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd like to give more thought to this interface, but we can leave for a follow up PR 🙂

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, let's do that in a follow-up.

RenzoMinelli and others added 3 commits October 9, 2026 12:35
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
passkey_authentication_spec covers the controller and store contract, and
cache_spec covers the Cache store without a session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@RenzoMinelli
RenzoMinelli merged commit 707ce1e into master Oct 9, 2026
31 checks passed
@RenzoMinelli
RenzoMinelli deleted the rm--cache-challenge-store branch October 9, 2026 20:30
RenzoMinelli added a commit that referenced this pull request Oct 13, 2026
- Accept `public_key_credential` as a JSON object or string.
- Respect `skip_session_storage` (`:params_auth`) in both WebAuthn
  strategies.
- Load the options controllers under an `ActionController::API` parent.

Moved from #152, which now only adds the cache challenge store.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RenzoMinelli added a commit that referenced this pull request Oct 13, 2026
- Accept `public_key_credential` as a JSON object or string.
- Respect `skip_session_storage` (`:params_auth`) in both WebAuthn
  strategies.
- Load the options controllers under an `ActionController::API` parent.

Moved from #152, which now only adds the cache challenge store.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RenzoMinelli added a commit that referenced this pull request Oct 13, 2026
- Accept `public_key_credential` as a JSON object or string.
- Respect `skip_session_storage` (`:params_auth`) in both WebAuthn
  strategies.
- Load the options controllers under an `ActionController::API` parent.

Moved from #152, which now only adds the cache challenge store.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RenzoMinelli added a commit that referenced this pull request Oct 13, 2026
- Accept `public_key_credential` as a JSON object or string.
- Respect `skip_session_storage` (`:params_auth`) in both WebAuthn
  strategies.
- Load the options controllers under an `ActionController::API` parent.

Moved from #152, which now only adds the cache challenge store.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RenzoMinelli added a commit that referenced this pull request Oct 13, 2026
- Accept `public_key_credential` as a JSON object or string.
- Respect `skip_session_storage` (`:params_auth`) in both WebAuthn
  strategies.
- Load the options controllers under an `ActionController::API` parent.

Moved from #152, which now only adds the cache challenge store.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

2 participants