Add redis support for distributed caching (#83)

* Add redis support for distributed caching

* Move towards the self-provided Redis connection pool and RESP protocol implementation.
Official redis client library won't work with yaegi.

* fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi.

* fixup! fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi.

* fixup! fixup! fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi.

* fixup! fixup! fixup! fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi.

* fixup! fixup! fixup! fixup! fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi.

* ... and another all nighter.

* fixup! ... and another all nighter.

* fixup! fixup! ... and another all nighter.

* fixup! fixup! fixup! ... and another all nighter.

* Resolve issue #85 by adding ability to set custom claims in JWT tokens

* Remove redundant validation in auth middleware ( issue #89 )

* Add ability to set cookie prefix for session cookies ( #87 )

* fixup! Add ability to set cookie prefix for session cookies ( #87 )

* Add ability to set cookie max age - issue #91

* Potential fix for code scanning alert no. 10: Size computation for allocation may overflow

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* fixup! Merge main into 0.8.0-redis: resolve conflicts

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
This commit is contained in:
2025-11-30 02:18:46 +00:00
committed by GitHub
parent 5fcbd54955
commit e64fc7f730
318 changed files with 100989 additions and 948 deletions
+17 -49
View File
@@ -967,7 +967,9 @@ func (cm *ChunkManager) validateTokenExpiration(token string, config TokenConfig
// Returns:
// - The expiration time if present, nil if no 'exp' claim.
// - An error if JWT parsing fails.
func (cm *ChunkManager) extractJWTExpiration(token string) (*time.Time, error) {
//
// extractJWTClaim extracts a time claim from a JWT token
func (cm *ChunkManager) extractJWTClaim(token, claimName string) (*time.Time, error) {
parts := strings.Split(token, ".")
if len(parts) != 3 {
return nil, fmt.Errorf("invalid JWT format")
@@ -988,25 +990,29 @@ func (cm *ChunkManager) extractJWTExpiration(token string) (*time.Time, error) {
return nil, fmt.Errorf("failed to parse JWT claims: %w", err)
}
exp, exists := claims["exp"]
claimValue, exists := claims[claimName]
if !exists {
return nil, nil
}
// Convert expiration to time.Time
var expTime time.Time
switch v := exp.(type) {
// Convert claim to time.Time
var claimTime time.Time
switch v := claimValue.(type) {
case float64:
expTime = time.Unix(int64(v), 0)
claimTime = time.Unix(int64(v), 0)
case int64:
expTime = time.Unix(v, 0)
claimTime = time.Unix(v, 0)
case int:
expTime = time.Unix(int64(v), 0)
claimTime = time.Unix(int64(v), 0)
default:
return nil, fmt.Errorf("invalid expiration format: %T", exp)
return nil, fmt.Errorf("invalid %s format: %T", claimName, claimValue)
}
return &expTime, nil
return &claimTime, nil
}
func (cm *ChunkManager) extractJWTExpiration(token string) (*time.Time, error) {
return cm.extractJWTClaim(token, "exp")
}
// validateTokenFreshness checks if token is fresh enough for storage.
@@ -1058,45 +1064,7 @@ func (cm *ChunkManager) validateTokenFreshness(token string, config TokenConfig)
// - The issued at time if present, nil if no 'iat' claim.
// - An error if JWT parsing fails.
func (cm *ChunkManager) extractJWTIssuedAt(token string) (*time.Time, error) {
parts := strings.Split(token, ".")
if len(parts) != 3 {
return nil, fmt.Errorf("invalid JWT format")
}
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return nil, fmt.Errorf("failed to decode JWT payload: %w", err)
}
// Parse the JSON payload using pooled decoder
var claims map[string]interface{}
pm := pool.Get()
decoder := pm.GetJSONDecoder(bytes.NewReader(payload))
defer pm.PutJSONDecoder(decoder)
if err := decoder.Decode(&claims); err != nil {
return nil, fmt.Errorf("failed to parse JWT claims: %w", err)
}
iat, exists := claims["iat"]
if !exists {
return nil, nil
}
// Convert issued at to time.Time
var iatTime time.Time
switch v := iat.(type) {
case float64:
iatTime = time.Unix(int64(v), 0)
case int64:
iatTime = time.Unix(v, 0)
case int:
iatTime = time.Unix(int64(v), 0)
default:
return nil, fmt.Errorf("invalid issued at format: %T", iat)
}
return &iatTime, nil
return cm.extractJWTClaim(token, "iat")
}
// CleanupExpiredSessions removes expired sessions to prevent memory leaks.