package auth import ( "crypto/rand" "encoding/hex" "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: hex.EncodeToString(b), ClientName: name, RedirectURIs: append([]string(nil), redirectURIs...), CreatedAt: time.Now(), } s.mu.Lock() s.clients[client.ClientID] = client s.mu.Unlock() return client, nil } // Lookup returns the client for the given client_id. func (s *DynamicClientStore) Lookup(clientID string) (DynamicClient, bool) { s.mu.RLock() client, ok := s.clients[clientID] s.mu.RUnlock() return client, ok }