refresh_tokens.go 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. // Package repository
  2. package repository
  3. import (
  4. "context"
  5. "crypto/sha256"
  6. "encoding/hex"
  7. "fmt"
  8. "time"
  9. "github.com/jackc/pgx/v5"
  10. "github.com/jackc/pgx/v5/pgxpool"
  11. "gogs.fxtmmsk.ru/foxtime/photoplaces/backend/internal/models"
  12. )
  13. type RefreshTokenRepo struct {
  14. pool *pgxpool.Pool
  15. }
  16. func NewRefreshTokenRepo(pool *pgxpool.Pool) *RefreshTokenRepo {
  17. return &RefreshTokenRepo{pool: pool}
  18. }
  19. func hashToken(token string) string {
  20. sum := sha256.Sum256([]byte(token))
  21. return hex.EncodeToString(sum[:])
  22. }
  23. func (r *RefreshTokenRepo) Create(ctx context.Context, userID, plainToken string, expiresAt time.Time) error {
  24. tokenHash := hashToken(plainToken)
  25. _, err := r.pool.Exec(ctx,
  26. `INSERT INTO refresh_tokens (user_id, token_hash, expires_at)
  27. VALUES ($1, $2, $3)`,
  28. userID, tokenHash, expiresAt,
  29. )
  30. if err != nil {
  31. return fmt.Errorf("create refresh token: %w", err)
  32. }
  33. return nil
  34. }
  35. func (r *RefreshTokenRepo) GetValid(ctx context.Context, plainToken string) (*models.RefreshToken, error) {
  36. tokenHash := hashToken(plainToken)
  37. row := r.pool.QueryRow(ctx,
  38. `SELECT id, user_id, token_hash, expires_at, created_at, revoked_at
  39. FROM refresh_tokens
  40. WHERE token_hash = $1 AND expires_at > now() AND revoked_at IS NULL`,
  41. tokenHash,
  42. )
  43. var rt models.RefreshToken
  44. err := row.Scan(&rt.ID, &rt.UserID, &rt.TokenHash, &rt.ExpiresAt, &rt.CreatedAt, &rt.RevokedAt)
  45. if err != nil {
  46. if err == pgx.ErrNoRows {
  47. return nil, nil
  48. }
  49. return nil, fmt.Errorf("get refresh token: %w", err)
  50. }
  51. return &rt, nil
  52. }
  53. func (r *RefreshTokenRepo) Revoke(ctx context.Context, tokenHash string) error {
  54. _, err := r.pool.Exec(ctx,
  55. `UPDATE refresh_tokens SET revoked_at = now() WHERE token_hash = $1 AND revoked_at IS NULL`,
  56. tokenHash,
  57. )
  58. return err
  59. }
  60. func (r *RefreshTokenRepo) RevokeAllForUser(ctx context.Context, userID string) error {
  61. _, err := r.pool.Exec(ctx,
  62. `UPDATE refresh_tokens SET revoked_at = now() WHERE user_id = $1 AND revoked_at IS NULL`,
  63. userID,
  64. )
  65. return err
  66. }
  67. func (r *RefreshTokenRepo) Delete(ctx context.Context, tokenHash string) error {
  68. _, err := r.pool.Exec(ctx,
  69. `DELETE FROM refresh_tokens WHERE token_hash = $1`,
  70. tokenHash,
  71. )
  72. return err
  73. }
  74. func (r *RefreshTokenRepo) CleanupExpired(ctx context.Context) error {
  75. _, err := r.pool.Exec(ctx,
  76. `DELETE FROM refresh_tokens WHERE expires_at < now() - interval '1 day'`,
  77. )
  78. return err
  79. }