golang-template/internal/services/auth.go

109 lines
2 KiB
Go

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
}