Skip to content

Feature: Pluggable QSessionStoreInterface QBit for session persistence #336

@KofTwentyTwo

Description

@KofTwentyTwo

Summary

Add a pluggable QSessionStoreProviderInterface QBit that allows applications to persist full QSession state (including security keys and custom data) between requests and across server restarts.

Background

Issue #334 added customizer support to OAuth2AuthenticationModule, enabling applications to inject security keys via customizeSession(). This works well when key derivation is cheap (static lookups or JWT claim extraction).

For applications with expensive key derivation (e.g., database queries, external API calls), re-deriving keys on every request may be suboptimal. This feature would allow applications to cache/persist the full session state.


Design Decisions

  • Location: Separate Maven module qqq-qbit-session-store
  • Integration: Explicit config via QAuthenticationMetaData.withSessionStoreEnabled(true)
  • Providers: In-Memory, Table-based, and Redis (Strategy Pattern)

Design Pattern: Strategy

┌─────────────────────────────────────────┐
│     QSessionStoreProviderInterface      │  ← Strategy Interface
│  (store, load, remove, touch, etc.)     │
└─────────────────────────────────────────┘
          ▲           ▲           ▲
          │           │           │
┌─────────┴──┐ ┌──────┴─────┐ ┌───┴────────┐
│ InMemory   │ │ TableBased │ │   Redis    │  ← Concrete Strategies
│ Provider   │ │  Provider  │ │  Provider  │
└────────────┘ └────────────┘ └────────────┘
                      ▲
                      │
              ┌───────┴───────┐
              │ Custom impl   │  ← User-provided via QCodeReference
              └───────────────┘

Proposed Interface

public interface QSessionStoreProviderInterface
{
   void store(String sessionUuid, QSession session, Duration ttl);
   Optional<QSession> load(String sessionUuid);
   void remove(String sessionUuid);
   void touch(String sessionUuid);  // sliding expiration
   void cleanExpired();
   String status();
   default void configure(QSessionStoreQBitConfig config) {}
}

Provider Implementations

  1. InMemorySessionStoreProvider - ConcurrentHashMap + LRU, for dev/single-server
  2. TableBasedSessionStoreProvider - QQQ table storage, for persistence
  3. RedisSessionStoreProvider - Jedis client, for distributed HA

QBitConfig Options

  • providerType - IN_MEMORY, TABLE_BASED, REDIS, or CUSTOM
  • defaultTtl - Session expiration (default: 1 hour)
  • enableSlidingExpiration - Reset TTL on access
  • backendName - For TABLE_BASED provider
  • redisHost, redisPort, redisPassword, redisKeyPrefix - For REDIS
  • customProviderCodeReference - For custom implementations

Usage Example

new QSessionStoreQBitProducer()
   .withConfig(new QSessionStoreQBitConfig()
      .withProviderType(QSessionStoreProviderType.TABLE_BASED)
      .withBackendName("primaryBackend")
      .withDefaultTtl(Duration.ofHours(8))
      .withEnableSlidingExpiration(true))
   .produce(qInstance);

Implementation TODO

Phase 1: Module Setup

  • Create qqq-qbit-session-store Maven module
  • Add to parent pom.xml modules list

Phase 2: Core Interfaces

  • QSessionStoreProviderInterface (Strategy interface)
  • QSessionStoreProviderType enum
  • QSessionStoreProviderFactory
  • QSessionStoreQBitConfig

Phase 3: Provider Implementations

  • InMemorySessionStoreProvider + tests
  • TableBasedSessionStoreProvider + StoredSession entity + tests
  • RedisSessionStoreProvider + tests (Testcontainers)

Phase 4: QBit Producer

  • QSessionStoreQBitProducer
  • CleanExpiredSessionsProcess

Phase 5: Auth Module Integration

  • Add sessionStoreEnabled to QAuthenticationMetaData
  • Add hooks to OAuth2AuthenticationModule
  • Add hooks to Auth0AuthenticationModule

Phase 6: Testing

  • Unit tests for all providers
  • Integration tests with auth modules
  • Backwards compatibility verification

Related

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

Status

Ready

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions