70 lines
2.2 KiB
Go
70 lines
2.2 KiB
Go
package team
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"encoding/base32"
|
|
"time"
|
|
|
|
"Yimaru-Backend/internal/domain"
|
|
)
|
|
|
|
func generateOpaqueRefreshToken() (string, error) {
|
|
b := make([]byte, 32)
|
|
if _, err := rand.Read(b); err != nil {
|
|
return "", err
|
|
}
|
|
return base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(b), nil
|
|
}
|
|
|
|
// IssueRefreshTokenOnLogin revokes prior active team refresh tokens for the member and stores a new one.
|
|
func (s *Service) IssueRefreshTokenOnLogin(ctx context.Context, memberID int64) (string, error) {
|
|
if err := s.teamStore.RevokeAllActiveTeamRefreshTokensForMember(ctx, memberID); err != nil {
|
|
return "", err
|
|
}
|
|
tok, err := generateOpaqueRefreshToken()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
now := time.Now()
|
|
exp := now.Add(time.Duration(s.refreshExpirySec) * time.Second)
|
|
if err := s.teamStore.CreateTeamRefreshToken(ctx, memberID, tok, exp, now); err != nil {
|
|
return "", err
|
|
}
|
|
return tok, nil
|
|
}
|
|
|
|
// RefreshSession validates a team refresh token, rotates it, and returns the member plus the new refresh token plaintext.
|
|
func (s *Service) RefreshSession(ctx context.Context, refreshTokenPlain string) (domain.TeamMember, string, error) {
|
|
rt, err := s.teamStore.GetTeamRefreshTokenByToken(ctx, refreshTokenPlain)
|
|
if err != nil {
|
|
return domain.TeamMember{}, "", err
|
|
}
|
|
if rt.Revoked {
|
|
return domain.TeamMember{}, "", domain.ErrTeamRefreshTokenNotFound
|
|
}
|
|
if time.Now().After(rt.ExpiresAt) {
|
|
return domain.TeamMember{}, "", domain.ErrTeamRefreshTokenExpired
|
|
}
|
|
if err := s.teamStore.RevokeTeamRefreshTokenByToken(ctx, refreshTokenPlain); err != nil {
|
|
return domain.TeamMember{}, "", err
|
|
}
|
|
newTok, err := generateOpaqueRefreshToken()
|
|
if err != nil {
|
|
return domain.TeamMember{}, "", err
|
|
}
|
|
now := time.Now()
|
|
exp := now.Add(time.Duration(s.refreshExpirySec) * time.Second)
|
|
if err := s.teamStore.CreateTeamRefreshToken(ctx, rt.TeamMemberID, newTok, exp, now); err != nil {
|
|
return domain.TeamMember{}, "", err
|
|
}
|
|
member, err := s.teamStore.GetTeamMemberByID(ctx, rt.TeamMemberID)
|
|
if err != nil {
|
|
return domain.TeamMember{}, "", err
|
|
}
|
|
if member.Status != domain.TeamMemberStatusActive {
|
|
return domain.TeamMember{}, "", domain.ErrInvalidTeamMemberStatus
|
|
}
|
|
return member, newTok, nil
|
|
}
|