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:
76
internal/auth/auth_code_store.go
Normal file
76
internal/auth/auth_code_store.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const authCodeTTL = 10 * time.Minute
|
||||
|
||||
// AuthCode holds a pending authorization code and its associated PKCE data.
|
||||
type AuthCode struct {
|
||||
ClientID string
|
||||
RedirectURI string
|
||||
Scope string
|
||||
CodeChallenge string
|
||||
CodeChallengeMethod string
|
||||
KeyID string
|
||||
ExpiresAt time.Time
|
||||
}
|
||||
|
||||
// AuthCodeStore issues single-use authorization codes for the OAuth 2.0
|
||||
// Authorization Code flow.
|
||||
type AuthCodeStore struct {
|
||||
mu sync.Mutex
|
||||
codes map[string]AuthCode
|
||||
}
|
||||
|
||||
func NewAuthCodeStore() *AuthCodeStore {
|
||||
s := &AuthCodeStore{codes: make(map[string]AuthCode)}
|
||||
go s.sweepLoop()
|
||||
return s
|
||||
}
|
||||
|
||||
// Issue stores the entry and returns the raw authorization code.
|
||||
func (s *AuthCodeStore) Issue(entry AuthCode) (string, error) {
|
||||
b := make([]byte, 16)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return "", err
|
||||
}
|
||||
raw := hex.EncodeToString(b)
|
||||
entry.ExpiresAt = time.Now().Add(authCodeTTL)
|
||||
s.mu.Lock()
|
||||
s.codes[raw] = entry
|
||||
s.mu.Unlock()
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
// Consume validates and removes the code, returning the associated entry.
|
||||
func (s *AuthCodeStore) Consume(code string) (AuthCode, bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
entry, ok := s.codes[code]
|
||||
if !ok || time.Now().After(entry.ExpiresAt) {
|
||||
delete(s.codes, code)
|
||||
return AuthCode{}, false
|
||||
}
|
||||
delete(s.codes, code)
|
||||
return entry, true
|
||||
}
|
||||
|
||||
func (s *AuthCodeStore) sweepLoop() {
|
||||
ticker := time.NewTicker(5 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
now := time.Now()
|
||||
s.mu.Lock()
|
||||
for code, entry := range s.codes {
|
||||
if now.After(entry.ExpiresAt) {
|
||||
delete(s.codes, code)
|
||||
}
|
||||
}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user