Files
amcs/internal/auth/dynamic_client_store.go

69 lines
1.7 KiB
Go

package auth
import (
"crypto/rand"
"fmt"
"strings"
"sync"
"time"
)
// DynamicClient holds a dynamically registered OAuth client (RFC 7591).
type DynamicClient struct {
ClientID string
ClientName string
RedirectURIs []string
CreatedAt time.Time
}
// HasRedirectURI reports whether uri is registered for this client.
func (c *DynamicClient) HasRedirectURI(uri string) bool {
for _, u := range c.RedirectURIs {
if u == uri {
return true
}
}
return false
}
// DynamicClientStore holds dynamically registered OAuth clients in memory.
type DynamicClientStore struct {
mu sync.RWMutex
clients map[string]DynamicClient
}
func NewDynamicClientStore() *DynamicClientStore {
return &DynamicClientStore{clients: make(map[string]DynamicClient)}
}
// Register creates a new client and returns it.
func (s *DynamicClientStore) Register(name string, redirectURIs []string) (DynamicClient, error) {
b := make([]byte, 16)
if _, err := rand.Read(b); err != nil {
return DynamicClient{}, err
}
client := DynamicClient{
ClientID: fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]),
ClientName: name,
RedirectURIs: append([]string(nil), redirectURIs...),
CreatedAt: time.Now(),
}
s.mu.Lock()
s.clients[normalizeClientID(client.ClientID)] = client
s.mu.Unlock()
return client, nil
}
// Lookup returns the client for the given client_id.
// Accepts UUIDs with or without dashes.
func (s *DynamicClientStore) Lookup(clientID string) (DynamicClient, bool) {
s.mu.RLock()
client, ok := s.clients[normalizeClientID(clientID)]
s.mu.RUnlock()
return client, ok
}
func normalizeClientID(id string) string {
return strings.ReplaceAll(id, "-", "")
}