Files
go-garminconnect/internal/auth/mfastate.go
2025-08-27 08:32:14 -07:00

82 lines
1.7 KiB
Go

package auth
import (
"encoding/json"
"os"
"path/filepath"
"time"
"sync"
)
// MFAState represents the state of an MFA verification session
type MFAState struct {
VerificationURL string `json:"verification_url"`
SessionToken string `json:"session_token"`
MFACode string `json:"mfa_code"`
ExpiresAt time.Time `json:"expires_at"`
}
// MFAStorage handles persistence of MFA state
type MFAStorage interface {
Store(state MFAState) error
Get() (MFAState, error)
Clear() error
}
// FileMFAStorage implements MFAStorage using a JSON file
type FileMFAStorage struct {
filePath string
mutex sync.RWMutex
}
// NewFileMFAStorage creates a new file-based MFA storage
func NewFileMFAStorage() *FileMFAStorage {
home, _ := os.UserHomeDir()
return &FileMFAStorage{
filePath: filepath.Join(home, ".garminconnect", "mfa_state.json"),
}
}
// Store saves MFA state to file
func (s *FileMFAStorage) Store(state MFAState) error {
s.mutex.Lock()
defer s.mutex.Unlock()
// Create directory if needed
dir := filepath.Dir(s.filePath)
if err := os.MkdirAll(dir, 0700); err != nil {
return err
}
data, err := json.MarshalIndent(state, "", " ")
if err != nil {
return err
}
return os.WriteFile(s.filePath, data, 0600)
}
// Get retrieves MFA state from file
func (s *FileMFAStorage) Get() (MFAState, error) {
s.mutex.RLock()
defer s.mutex.RUnlock()
data, err := os.ReadFile(s.filePath)
if err != nil {
if os.IsNotExist(err) {
return MFAState{}, nil
}
return MFAState{}, err
}
var state MFAState
err = json.Unmarshal(data, &state)
return state, err
}
// Clear removes the MFA state file
func (s *FileMFAStorage) Clear() error {
s.mutex.Lock()
defer s.mutex.Unlock()
return os.Remove(s.filePath)
}