authentification OpenID Connect (Keycloak) : flux authorization code, state signé, validation du token ID via JWKS, provisioning automatique des utilisateurs
This commit is contained in:
1 parent
51bbb45b32
commit
bedb6c98e7
15 files changed
+1057
-23
No files matched your search
@@ -0,0 +1,88 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
)
|
||||
|
||||
// providerMetadata est le document de découverte OpenID
|
||||
// (.well-known/openid-configuration).
|
||||
type providerMetadata struct {
|
||||
Issuer string `json:"issuer"`
|
||||
AuthorizationEndpoint string `json:"authorization_endpoint"`
|
||||
TokenEndpoint string `json:"token_endpoint"`
|
||||
JwksURI string `json:"jwks_uri"`
|
||||
}
|
||||
|
||||
type metadataCacheEntry struct {
|
||||
metadata *providerMetadata
|
||||
expiresAt time.Time
|
||||
}
|
||||
|
||||
// discovery maintient un cache des documents de découverte par fournisseur.
|
||||
type discovery struct {
|
||||
httpClient *http.Client
|
||||
mu sync.Mutex
|
||||
entries map[string]*metadataCacheEntry
|
||||
}
|
||||
|
||||
func newDiscovery() *discovery {
|
||||
return &discovery{
|
||||
httpClient: &http.Client{Timeout: 10 * time.Second},
|
||||
entries: make(map[string]*metadataCacheEntry),
|
||||
}
|
||||
}
|
||||
|
||||
// Metadata retourne les endpoints du fournisseur, avec mise en cache.
|
||||
func (d *discovery) Metadata(provider *domain.OIDCProvider) (*providerMetadata, error) {
|
||||
d.mu.Lock()
|
||||
entry, ok := d.entries[provider.ID]
|
||||
if ok && time.Now().Before(entry.expiresAt) {
|
||||
d.mu.Unlock()
|
||||
return entry.metadata, nil
|
||||
}
|
||||
d.mu.Unlock()
|
||||
|
||||
metadata, err := d.fetch(provider.WellknownURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching provider discovery document: %s", err)
|
||||
}
|
||||
if metadata.Issuer == "" || metadata.AuthorizationEndpoint == "" || metadata.TokenEndpoint == "" || metadata.JwksURI == "" {
|
||||
return nil, fmt.Errorf("incomplete discovery document for provider %s", provider.ID)
|
||||
}
|
||||
|
||||
d.mu.Lock()
|
||||
d.entries[provider.ID] = &metadataCacheEntry{
|
||||
metadata: metadata,
|
||||
expiresAt: time.Now().Add(1 * time.Hour),
|
||||
}
|
||||
d.mu.Unlock()
|
||||
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
func (d *discovery) fetch(wellknownURL string) (*providerMetadata, error) {
|
||||
res, err := d.httpClient.Get(wellknownURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("unexpected status %d", res.StatusCode)
|
||||
}
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metadata := &providerMetadata{}
|
||||
if err := json.Unmarshal(body, metadata); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return metadata, nil
|
||||
}
|
||||
Reference in new issue
Block a user