Documentation
¶
Overview ¶
Package cache stores WebAuthn ceremony state in a cache.Cache.
It is the store for a deployment that has redis and no relational database. Ceremony state is small, lives for about a minute, and is discarded on use, which is the shape a cache is best at — and unlike the session table, nothing here has to be swept, because the cache reclaims its own entries.
Where it stops ¶
Consume is a read followed by a delete, not one operation, because the cache.Cache seam has nothing that fetches and removes atomically. Two requests answering the same challenge in the same instant can therefore both be handed the ceremony, where authentication/webauthn/database hands it to exactly one. The window is the microseconds between the two round trips, and what fits in it is a replay of an assertion the legitimate user just made — so the practical cost is that one authentication may be counted twice, not that a stale assertion becomes usable later. A deployment that wants the stronger guarantee uses the database store, and this package declares the deviation to the conformance suite rather than quietly passing it.
A memory cache is not a deployment ¶
Backed by cache/memory this store is per-process, which is exactly the failure the WebAuthn ceremony is prone to: the challenge is issued by one replica and answered on another, and the login fails for no reason the user can act on. Use redis, or use the database store. Memory is for tests and for a single-process service.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNilCache = platformerrors.Wrap(platformerrors.ErrNilInputParameter, "nil webauthn session cache")
ErrNilCache indicates NewSessionStore was called without a cache. It wraps errors.ErrNilInputParameter, so a caller may check either.
Functions ¶
This section is empty.
Types ¶
type Option ¶
type Option func(*options)
Option configures the store at construction.
There is no WithMetricsProvider. Every count worth having here describes what a ceremony meant — begun, finished, refused — and only the relying party knows that; this layer would count round trips the cache provider already counts.
func WithLogger ¶
WithLogger attaches a logger. An absent logger logs nowhere.
func WithTracerProvider ¶
WithTracerProvider attaches a tracer provider. An absent one traces nowhere.
type SessionStore ¶
type SessionStore struct {
// contains filtered or unexported fields
}
SessionStore keeps WebAuthn ceremony state in a cache.
It is exported, and returned by NewSessionStore, so a caller who has chosen cache-backed ceremony state can depend on that choice rather than on the webauthn.SessionStore seam.
func NewSessionStore ¶
func NewSessionStore(c cache.Cache[webauthn.SessionData], opts ...Option) (*SessionStore, error)
NewSessionStore builds a SessionStore over a cache.
The cache is required and has no default, because which one it is decides whether the store works at all: see the package documentation on why a memory cache is not a deployment.
The cache's own default expiry is never used — every write carries the ceremony's remaining time — so a cache built solely for this can be configured with any expiry at all.
func (*SessionStore) Consume ¶
func (s *SessionStore) Consume(ctx context.Context, challenge string) (*webauthn.SessionData, error)
Consume returns the state stored under challenge and removes it.
The delete is not conditional on the read having won a race, because the cache seam offers no way to make it so. It is, however, unconditional in the other direction: the entry is removed even though this caller may not be the only one holding it, so a replay arriving a moment later still finds nothing.
func (*SessionStore) Save ¶
func (s *SessionStore) Save(ctx context.Context, session *webauthn.SessionData, ttl time.Duration) error
Save stores a ceremony's state under its own challenge for ttl.