Documentation
¶
Overview ¶
Package memory provides an in-memory implementation of the OAuth storage interfaces.
This package implements TokenStore, ClientStore, and FlowStore interfaces using Go's built-in maps with mutex protection for thread safety. It is suitable for development, testing, and single-instance deployments where persistence is not required.
Features:
- Thread-safe operations using sync.RWMutex
- Automatic cleanup of expired tokens, codes, and flows
- Configurable cleanup intervals
- Token encryption support via Encryptor
- Audit logging support via Auditor
For production deployments requiring persistence or multi-instance deployments, use the storage/valkey package instead.
Example usage:
store := memory.New() defer store.Stop() // Use store for TokenStore, ClientStore, and FlowStore interfaces server, _ := oauth.NewServer(provider, store, store, store, config, logger)
Package memory provides an in-memory implementation of all storage interfaces. It is suitable for development, testing, and single-instance deployments.
Index ¶
- type RefreshTokenFamily
- type Store
- func (s *Store) AtomicCheckAndMarkAuthCodeUsed(_ context.Context, code string) (*storage.AuthorizationCode, error)
- func (s *Store) AtomicGetAndDeleteRefreshToken(_ context.Context, refreshToken string) (string, string, *oauth2.Token, error)
- func (s *Store) CheckIPLimit(_ context.Context, ip string, maxClientsPerIP int) error
- func (s *Store) DeleteAuthorizationCode(_ context.Context, code string) error
- func (s *Store) DeleteAuthorizationState(_ context.Context, stateID string) error
- func (s *Store) DeleteRefreshToken(_ context.Context, refreshToken string) error
- func (s *Store) DeleteToken(ctx context.Context, userID string) error
- func (s *Store) GetActiveRefreshTokenByFamily(_ context.Context, familyID string) (refreshToken, clientID string, err error)
- func (s *Store) GetAuthorizationCode(_ context.Context, code string) (*storage.AuthorizationCode, error)
- func (s *Store) GetAuthorizationState(_ context.Context, stateID string) (*storage.AuthorizationState, error)
- func (s *Store) GetAuthorizationStateByProviderState(_ context.Context, providerState string) (*storage.AuthorizationState, error)
- func (s *Store) GetClient(ctx context.Context, clientID string) (*storage.Client, error)
- func (s *Store) GetRefreshTokenFamily(_ context.Context, refreshToken string) (*storage.RefreshTokenFamilyMetadata, error)
- func (s *Store) GetRefreshTokenFamilyByID(_ context.Context, familyID string) (*storage.RefreshTokenFamilyMetadata, error)
- func (s *Store) GetRefreshTokenInfo(_ context.Context, refreshToken string) (string, error)
- func (s *Store) GetToken(ctx context.Context, userID string) (*oauth2.Token, error)
- func (s *Store) GetTokenMetadata(tokenID string) (*storage.TokenMetadata, error)
- func (s *Store) GetTokensByUserClient(_ context.Context, userID, clientID string) ([]string, error)
- func (s *Store) GetUserInfo(ctx context.Context, userID string) (*providers.UserInfo, error)
- func (s *Store) IsJTIRevoked(_ context.Context, jti string) (bool, error)
- func (s *Store) ListClients(_ context.Context) ([]*storage.Client, error)
- func (s *Store) LogValue() slog.Value
- func (s *Store) RevokeAllTokensForUserClient(_ context.Context, userID, clientID string) (int, error)
- func (s *Store) RevokeJTI(_ context.Context, jti string, expiresAt time.Time) error
- func (s *Store) RevokeRefreshTokenFamily(_ context.Context, familyID string) error
- func (s *Store) SaveAuthorizationCode(ctx context.Context, code *storage.AuthorizationCode) error
- func (s *Store) SaveAuthorizationState(_ context.Context, state *storage.AuthorizationState) error
- func (s *Store) SaveClient(ctx context.Context, client *storage.Client) error
- func (s *Store) SaveRefreshToken(_ context.Context, refreshToken, userID string, expiresAt time.Time) error
- func (s *Store) SaveRefreshTokenWithFamily(_ context.Context, refreshToken, userID, clientID, familyID string, ...) error
- func (s *Store) SaveToken(ctx context.Context, userID string, token *oauth2.Token) error
- func (s *Store) SaveTokenMetadata(_ context.Context, tokenID string, metadata storage.TokenMetadata) error
- func (s *Store) SaveUserInfo(ctx context.Context, userID string, info *providers.UserInfo) error
- func (s *Store) SetEncryptor(enc *security.Encryptor)
- func (s *Store) SetInstrumentation(inst *instrumentation.Instrumentation)
- func (s *Store) SetLogger(logger *slog.Logger)
- func (s *Store) SetRevokedFamilyRetentionDays(days int64)
- func (s *Store) Stop()
- func (s *Store) TrackClientIP(ip string)
- func (s *Store) ValidateClientSecret(ctx context.Context, clientID, clientSecret string) error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type RefreshTokenFamily ¶
type RefreshTokenFamily struct {
FamilyID string // Unique identifier for this token family
UserID string // User who owns this family
ClientID string // Client who owns this family
Generation int // Increments with each rotation
IssuedAt time.Time // When this generation was issued
Revoked bool // True if family has been revoked due to reuse detection
RevokedAt time.Time // When this family was revoked (for cleanup purposes)
}
RefreshTokenFamily tracks a family of refresh tokens for reuse detection (OAuth 2.1)
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is an in-memory implementation of all storage interfaces. It implements TokenStore, ClientStore, FlowStore, RefreshTokenFamilyStore, and TokenRevocationStore.
func New ¶
func New() *Store
New creates a new in-memory store with default cleanup interval (1 minute) and default revoked family retention (90 days)
func NewWithInterval ¶
NewWithInterval creates a new in-memory store with custom cleanup interval. If cleanupInterval is 0 or negative, uses default of 1 minute.
func (*Store) AtomicCheckAndMarkAuthCodeUsed ¶ added in v0.1.11
func (s *Store) AtomicCheckAndMarkAuthCodeUsed(_ context.Context, code string) (*storage.AuthorizationCode, error)
AtomicCheckAndMarkAuthCodeUsed atomically checks if a code is unused and marks it as used. This prevents race conditions in authorization code reuse detection. Returns the auth code if successful, or an error if code is already used.
SECURITY: This operation is atomic - only ONE concurrent request can succeed. All other concurrent requests will receive an "already used" error.
IMPORTANT: The authCode is ONLY returned on reuse errors (Used=true) to enable detection and revocation. For other errors (not found, expired), nil is returned to prevent information leakage.
func (*Store) AtomicGetAndDeleteRefreshToken ¶ added in v0.1.11
func (s *Store) AtomicGetAndDeleteRefreshToken(_ context.Context, refreshToken string) (string, string, *oauth2.Token, error)
AtomicGetAndDeleteRefreshToken atomically retrieves and deletes a refresh token. This prevents race conditions in refresh token rotation and reuse detection. Returns the userID, clientID, and provider token if successful.
SECURITY: This operation is atomic - only ONE concurrent request can succeed. All other concurrent requests will receive a "token not found" error. SECURITY: Returns clientID for client binding validation per OAuth 2.1 Section 6.
func (*Store) CheckIPLimit ¶
CheckIPLimit checks if an IP has reached the client registration limit
func (*Store) DeleteAuthorizationCode ¶
DeleteAuthorizationCode removes an authorization code
func (*Store) DeleteAuthorizationState ¶
DeleteAuthorizationState removes an authorization state Removes both client state and provider state entries
func (*Store) DeleteRefreshToken ¶
DeleteRefreshToken removes a refresh token (used for rotation)
func (*Store) DeleteToken ¶
DeleteToken removes a token for a user
func (*Store) GetActiveRefreshTokenByFamily ¶ added in v0.2.122
func (s *Store) GetActiveRefreshTokenByFamily(_ context.Context, familyID string) (refreshToken, clientID string, err error)
GetActiveRefreshTokenByFamily returns the most recent (highest generation) non-revoked refresh token for the family along with the owning client ID (storage.ActiveRefreshTokenByFamilyStore). Linear scan, same shape as GetRefreshTokenFamilyByID.
Returns ErrRefreshTokenFamilyNotFound when no entry matches at all, or ErrRefreshTokenFamilyRevoked when entries exist but every member is revoked.
func (*Store) GetAuthorizationCode ¶
func (s *Store) GetAuthorizationCode(_ context.Context, code string) (*storage.AuthorizationCode, error)
GetAuthorizationCode retrieves an authorization code without modifying it. The code is kept marked as "Used" to detect reuse attempts (OAuth 2.1 requirement). Expired/used codes are cleaned up by the background cleanup goroutine.
NOTE: For actual code exchange, use AtomicCheckAndMarkAuthCodeUsed instead to prevent race conditions.
func (*Store) GetAuthorizationState ¶
func (s *Store) GetAuthorizationState(_ context.Context, stateID string) (*storage.AuthorizationState, error)
GetAuthorizationState retrieves an authorization state by client state
func (*Store) GetAuthorizationStateByProviderState ¶
func (s *Store) GetAuthorizationStateByProviderState(_ context.Context, providerState string) (*storage.AuthorizationState, error)
GetAuthorizationStateByProviderState retrieves an authorization state by provider state This is used during provider callback validation (separate from client state)
func (*Store) GetRefreshTokenFamily ¶
func (s *Store) GetRefreshTokenFamily(_ context.Context, refreshToken string) (*storage.RefreshTokenFamilyMetadata, error)
GetRefreshTokenFamily retrieves family metadata for a refresh token
func (*Store) GetRefreshTokenFamilyByID ¶ added in v0.2.120
func (s *Store) GetRefreshTokenFamilyByID(_ context.Context, familyID string) (*storage.RefreshTokenFamilyMetadata, error)
GetRefreshTokenFamilyByID returns family metadata indexed by family ID (storage.RefreshTokenFamilyByIDStore). The internal map is keyed by refresh token, so this is a linear scan over an entry count bounded by hardMaxFamilyMetadataEntries (50000). Cheap in practice; if it ever shows up in a profile, add a parallel familyID -> refreshToken index.
func (*Store) GetRefreshTokenInfo ¶
GetRefreshTokenInfo retrieves the user ID for a refresh token Returns error if token is not found or expired (with clock skew grace)
func (*Store) GetTokenMetadata ¶ added in v0.1.34
func (s *Store) GetTokenMetadata(tokenID string) (*storage.TokenMetadata, error)
GetTokenMetadata retrieves metadata for a token (including RFC 8707 audience)
func (*Store) GetTokensByUserClient ¶ added in v0.1.9
GetTokensByUserClient retrieves all token IDs for a user+client combination. This is primarily for testing and debugging purposes.
func (*Store) GetUserInfo ¶
GetUserInfo retrieves user information
func (*Store) IsJTIRevoked ¶ added in v0.2.120
IsJTIRevoked implements storage.RevokedTokenStore.
func (*Store) ListClients ¶
ListClients lists all registered clients
func (*Store) LogValue ¶ added in v0.2.123
LogValue implements slog.LogValuer so callers can emit a one-shot structured snapshot of the store's posture:
logger.Info("storage initialized", "store", store)
func (*Store) RevokeAllTokensForUserClient ¶ added in v0.1.9
func (s *Store) RevokeAllTokensForUserClient(_ context.Context, userID, clientID string) (int, error)
RevokeAllTokensForUserClient revokes all tokens (access + refresh) for a specific user+client combination. This implements the OAuth 2.1 requirement for authorization code reuse detection. Returns the number of tokens revoked and any error encountered.
func (*Store) RevokeRefreshTokenFamily ¶
RevokeRefreshTokenFamily revokes all tokens in a family (for reuse detection) This is called when token reuse is detected (OAuth 2.1 security requirement)
func (*Store) SaveAuthorizationCode ¶
SaveAuthorizationCode saves an issued authorization code
func (*Store) SaveAuthorizationState ¶
SaveAuthorizationState saves the state of an ongoing authorization flow Stores by both client state (StateID) and provider state (ProviderState) for dual lookup
func (*Store) SaveClient ¶
SaveClient saves a registered client and tracks IP for DoS protection
func (*Store) SaveRefreshToken ¶
func (s *Store) SaveRefreshToken(_ context.Context, refreshToken, userID string, expiresAt time.Time) error
SaveRefreshToken saves a refresh token mapping to user ID with expiry For OAuth 2.1 compliance, also tracks token family for reuse detection
func (*Store) SaveRefreshTokenWithFamily ¶
func (s *Store) SaveRefreshTokenWithFamily(_ context.Context, refreshToken, userID, clientID, familyID string, generation int, expiresAt time.Time) error
SaveRefreshTokenWithFamily saves a refresh token with family tracking for reuse detection This is the OAuth 2.1 compliant version that enables token theft detection
func (*Store) SaveTokenMetadata ¶ added in v0.1.9
func (s *Store) SaveTokenMetadata(_ context.Context, tokenID string, metadata storage.TokenMetadata) error
SaveTokenMetadata saves metadata for a token. Implements storage.TokenMetadataStore. The metadata's IssuedAt is overwritten with time.Now() at the call site so callers do not have to populate it.
func (*Store) SaveUserInfo ¶
SaveUserInfo saves user information
func (*Store) SetEncryptor ¶
SetEncryptor sets the token encryptor for encryption at rest
func (*Store) SetInstrumentation ¶ added in v0.1.26
func (s *Store) SetInstrumentation(inst *instrumentation.Instrumentation)
SetInstrumentation sets OpenTelemetry instrumentation for the store
func (*Store) SetRevokedFamilyRetentionDays ¶ added in v0.1.11
SetRevokedFamilyRetentionDays sets the retention period for revoked token family metadata. This should be called after New() and before starting the server. The retention period is used for forensics and security auditing. Default: 90 days (if not set)
func (*Store) TrackClientIP ¶
TrackClientIP increments the client count for an IP address