Files
dex/examples/example-app/session/device.go
Maksim Nabokikh 6f2e233c7a feat: example app session refactoring (#4712)
Signed-off-by: maksim.nabokikh <max.nabokih@gmail.com>
2026-04-02 14:19:10 +02:00

105 lines
2.3 KiB
Go

package session
import (
"crypto/rand"
"encoding/hex"
"fmt"
"sync"
"time"
"golang.org/x/oauth2"
)
// DeviceState holds the state of an active Device Code Flow.
type DeviceState struct {
DeviceCode string
UserCode string
VerificationURI string
PollInterval int
Token *oauth2.Token
}
// DeviceStore manages Device Code Flow sessions.
type DeviceStore interface {
// Save stores a new session and returns its ID.
// Previous sessions are considered invalidated.
Save(state DeviceState) string
// Get returns a session by ID. Returns false if the session
// is not found or has been invalidated by a newer one.
Get(sessionID string) (DeviceState, bool)
// GetLatest returns the most recent session and its ID.
// Returns false if no session exists.
GetLatest() (string, DeviceState, bool)
// SetToken attaches a token to the session.
// Returns false if the session is not found or not current.
SetToken(sessionID string, token *oauth2.Token) bool
}
// memoryDeviceStore is an in-memory DeviceStore that supports
// a single active session at a time.
type memoryDeviceStore struct {
mu sync.Mutex
sessionID string
state DeviceState
}
// NewMemoryDeviceStore creates an in-memory DeviceStore.
func NewMemoryDeviceStore() DeviceStore {
return &memoryDeviceStore{}
}
func (s *memoryDeviceStore) Save(state DeviceState) string {
id := generateSessionID()
s.mu.Lock()
defer s.mu.Unlock()
s.sessionID = id
s.state = state
return id
}
func (s *memoryDeviceStore) Get(sessionID string) (DeviceState, bool) {
s.mu.Lock()
defer s.mu.Unlock()
if sessionID != s.sessionID {
return DeviceState{}, false
}
return s.state, true
}
func (s *memoryDeviceStore) GetLatest() (string, DeviceState, bool) {
s.mu.Lock()
defer s.mu.Unlock()
if s.sessionID == "" {
return "", DeviceState{}, false
}
return s.sessionID, s.state, true
}
func (s *memoryDeviceStore) SetToken(sessionID string, token *oauth2.Token) bool {
s.mu.Lock()
defer s.mu.Unlock()
if sessionID != s.sessionID {
return false
}
s.state.Token = token
return true
}
// generateSessionID creates a random session identifier.
func generateSessionID() string {
b := make([]byte, 16)
if _, err := rand.Read(b); err != nil {
return fmt.Sprintf("%d", time.Now().UnixNano())
}
return hex.EncodeToString(b)
}