package session import ( "net/http" "sync" "time" "astraltech.xyz/accountmanager/src/logging" "astraltech.xyz/accountmanager/src/store" ) const SessionCookieName = "session_token" type SessionManager struct { store store.KeyValueStore[*SessionData] } var instance *SessionManager var once sync.Once type StoreType int const ( InMemory StoreType = iota Redis ) func GetSessionManager() *SessionManager { once.Do(func() { instance = &SessionManager{} }) return instance } func (manager *SessionManager) SetStoreType(storeType StoreType, params ...any) { logging.Infof("Changing session manager store type") switch storeType { case InMemory: { manager.store = store.NewMemoryStore[*SessionData]() break } case Redis: { url, _ := params[0].(string) prefix, _ := params[1].(string) manager.store = store.NewRedisStore[*SessionData](url, prefix) break } } } func (manager *SessionManager) CreateSession(userID string) (cookie *http.Cookie, err error) { logging.Debugf("Creating a new session for %s", userID) token, err := GenerateSessionToken(32) // Use crypto/rand for this if err != nil { return nil, err } CSRFToken, err := GenerateSessionToken(32) if err != nil { return nil, err } newSessionData := SessionData{ UserID: userID, CSRFToken: CSRFToken, ExpiresAt: time.Now().Add(time.Hour), } err = manager.store.CreateExpiring(token, &newSessionData, time.Hour) if err != nil { return nil, err } newCookie := &http.Cookie{ Name: SessionCookieName, Value: token, Path: "/", HttpOnly: true, Secure: true, SameSite: http.SameSiteLaxMode, MaxAge: 3600, } return newCookie, nil } func (manager *SessionManager) GetSession(r *http.Request) (*SessionData, error) { logging.Debug("Validating session from request") cookie, err := r.Cookie(SessionCookieName) if err != nil { return nil, ErrSessionNotFound } token := cookie.Value if token == "" { return nil, ErrSessionNotFound } data, err := manager.store.Get(token) if err != nil { return nil, ErrSessionNotFound } if time.Now().After(data.ExpiresAt) { _ = manager.store.Delete(token) return nil, ErrSessionExpired } return data, nil } func (manager *SessionManager) DeleteSession(sessionId string) error { return manager.store.Delete(sessionId) }