Something went wrong. Try again.
Monorepo for Tangled — https://tangled.org forked from tangled.org/core
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148package oauth
import ( "context" "encoding/json" "fmt" "time"
"github.com/bluesky-social/indigo/atproto/auth/oauth" "github.com/bluesky-social/indigo/atproto/syntax" "github.com/redis/go-redis/v9")
// redis-backed implementation of ClientAuthStore.type RedisStore struct { client *redis.Client SessionTTL time.Duration AuthRequestTTL time.Duration}
var _ oauth.ClientAuthStore = &RedisStore{}
func NewRedisStore(redisURL string) (*RedisStore, error) { opts, err := redis.ParseURL(redisURL) if err != nil { return nil, fmt.Errorf("failed to parse redis URL: %w", err) }
client := redis.NewClient(opts)
// test the connection ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel()
if err := client.Ping(ctx).Err(); err != nil { return nil, fmt.Errorf("failed to connect to redis: %w", err) }
return &RedisStore{ client: client, SessionTTL: 30 * 24 * time.Hour, // 30 days AuthRequestTTL: 10 * time.Minute, // 10 minutes }, nil}
func (r *RedisStore) Close() error { return r.client.Close()}
func sessionKey(did syntax.DID, sessionID string) string { return fmt.Sprintf("oauth:session:%s:%s", did, sessionID)}
func authRequestKey(state string) string { return fmt.Sprintf("oauth:auth_request:%s", state)}
func (r *RedisStore) GetSession(ctx context.Context, did syntax.DID, sessionID string) (*oauth.ClientSessionData, error) { key := sessionKey(did, sessionID) data, err := r.client.Get(ctx, key).Bytes() if err == redis.Nil { return nil, fmt.Errorf("session not found: %s", did) } if err != nil { return nil, fmt.Errorf("failed to get session: %w", err) }
var sess oauth.ClientSessionData if err := json.Unmarshal(data, &sess); err != nil { return nil, fmt.Errorf("failed to unmarshal session: %w", err) }
return &sess, nil}
func (r *RedisStore) SaveSession(ctx context.Context, sess oauth.ClientSessionData) error { key := sessionKey(sess.AccountDID, sess.SessionID)
data, err := json.Marshal(sess) if err != nil { return fmt.Errorf("failed to marshal session: %w", err) }
if err := r.client.Set(ctx, key, data, r.SessionTTL).Err(); err != nil { return fmt.Errorf("failed to save session: %w", err) }
return nil}
func (r *RedisStore) DeleteSession(ctx context.Context, did syntax.DID, sessionID string) error { key := sessionKey(did, sessionID) if err := r.client.Del(ctx, key).Err(); err != nil { return fmt.Errorf("failed to delete session: %w", err) } return nil}
func (r *RedisStore) GetAuthRequestInfo(ctx context.Context, state string) (*oauth.AuthRequestData, error) { key := authRequestKey(state) data, err := r.client.Get(ctx, key).Bytes() if err == redis.Nil { return nil, fmt.Errorf("request info not found: %s", state) } if err != nil { return nil, fmt.Errorf("failed to get auth request: %w", err) }
var req oauth.AuthRequestData if err := json.Unmarshal(data, &req); err != nil { return nil, fmt.Errorf("failed to unmarshal auth request: %w", err) }
return &req, nil}
func (r *RedisStore) SaveAuthRequestInfo(ctx context.Context, info oauth.AuthRequestData) error { key := authRequestKey(info.State)
// check if already exists (to match MemStore behavior) exists, err := r.client.Exists(ctx, key).Result() if err != nil { return fmt.Errorf("failed to check auth request existence: %w", err) } if exists > 0 { return fmt.Errorf("auth request already saved for state %s", info.State) }
data, err := json.Marshal(info) if err != nil { return fmt.Errorf("failed to marshal auth request: %w", err) }
if err := r.client.Set(ctx, key, data, r.AuthRequestTTL).Err(); err != nil { return fmt.Errorf("failed to save auth request: %w", err) }
return nil}
func (r *RedisStore) DeleteAuthRequestInfo(ctx context.Context, state string) error { key := authRequestKey(state) if err := r.client.Del(ctx, key).Err(); err != nil { return fmt.Errorf("failed to delete auth request: %w", err) } return nil}