feat(auth): implement OAuth 2.0 authorization code flow and dynamic client registration

- Add OAuth 2.0 support with authorization code flow and dynamic client registration.
- Introduce new handlers for OAuth metadata, client registration, authorization, and token issuance.
- Enhance authentication middleware to support OAuth client credentials.
- Create in-memory stores for authorization codes and tokens.
- Update configuration to include OAuth client details.
- Ensure validation checks for OAuth clients in the configuration.
This commit is contained in:
2026-03-26 21:17:55 +02:00
parent ed05d390b7
commit 56c84df342
19 changed files with 970 additions and 40 deletions

View File

@@ -0,0 +1,62 @@
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
}