package services import ( "context" "crypto/subtle" "database/sql" "errors" "strings" "go-backend/internal/auth" "go-backend/internal/database/sqlc" ) type AuthService struct { db *sql.DB queries *sqlc.Queries } func NewAuthService(db *sql.DB, queries *sqlc.Queries) *AuthService { return &AuthService{ db: db, queries: queries, } } type SessionWithToken struct { Session sqlc.UserSession Token string } func (s *AuthService) CreateSession(ctx context.Context, userID string) (*SessionWithToken, error) { id, err := auth.GenerateSecureRandomString() if err != nil { return nil, err } secret, err := auth.GenerateSecureRandomString() if err != nil { return nil, err } secretHash := auth.HashSecret(secret) token := id + "." + secret session, err := s.queries.InsertSession(ctx, sqlc.InsertSessionParams{ ID: id, SecretHash: secretHash, UserID: userID, }) if err != nil { return nil, err } return &SessionWithToken{ Session: session, Token: token, }, nil } func (s *AuthService) ValidateSessionToken(ctx context.Context, token string) (*sqlc.UserSession, error) { tokenParts := strings.Split(token, ".") if len(tokenParts) != 2 { return nil, nil } sessionID := tokenParts[0] sessionSecret := tokenParts[1] session, err := s.queries.SelectSessionById(ctx, sessionID) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, nil } return nil, err } if auth.CheckExpiration(session.CreatedAt) { err = s.queries.DeleteSessionById(ctx, sessionID) if err != nil { return nil, err } return nil, nil } tokenSecretHash := auth.HashSecret(sessionSecret) if subtle.ConstantTimeCompare(tokenSecretHash, session.SecretHash) != 1 { return nil, nil } return &session, nil } func (s *AuthService) DeleteSession(ctx context.Context, token string) error { tokenParts := strings.Split(token, ".") if len(tokenParts) != 2 { return nil } sessionID := tokenParts[0] err := s.queries.DeleteSessionById(ctx, sessionID) return err }