Files
lti-api/internal/modules/sso/session/store.go
T
2025-10-06 12:31:54 +07:00

71 lines
1.8 KiB
Go

package session
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/redis/go-redis/v9"
)
const keyTemplate = "sso:pkce:%s"
// PKCESession holds data required to complete the OAuth2 PKCE exchange.
type PKCESession struct {
CodeVerifier string `json:"code_verifier"`
Nonce string `json:"nonce"`
ClientAlias string `json:"client_alias"`
ClientID string `json:"client_id"`
RedirectURI string `json:"redirect_uri"`
Scope string `json:"scope"`
ReturnTo string `json:"return_to,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
// Store persists pkce sessions inside Redis using a configurable TTL.
type Store struct {
redis *redis.Client
ttl time.Duration
}
func NewStore(client *redis.Client, ttl time.Duration) *Store {
return &Store{redis: client, ttl: ttl}
}
func (s *Store) Save(ctx context.Context, state string, payload *PKCESession) error {
if s.redis == nil {
return fmt.Errorf("redis client is not initialised")
}
bytes, err := json.Marshal(payload)
if err != nil {
return err
}
return s.redis.Set(ctx, fmt.Sprintf(keyTemplate, state), bytes, s.ttl).Err()
}
func (s *Store) Get(ctx context.Context, state string) (*PKCESession, error) {
if s.redis == nil {
return nil, fmt.Errorf("redis client is not initialised")
}
raw, err := s.redis.Get(ctx, fmt.Sprintf(keyTemplate, state)).Result()
if err != nil {
if err == redis.Nil {
return nil, nil
}
return nil, err
}
var payload PKCESession
if err := json.Unmarshal([]byte(raw), &payload); err != nil {
return nil, err
}
return &payload, nil
}
func (s *Store) Delete(ctx context.Context, state string) error {
if s.redis == nil {
return fmt.Errorf("redis client is not initialised")
}
return s.redis.Del(ctx, fmt.Sprintf(keyTemplate, state)).Err()
}