mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-17 04:37:48 +00:00
Add tests
This commit is contained in:
@@ -14,6 +14,14 @@ import (
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
type TokenVerifier interface {
|
||||
VerifyToken(token string) error
|
||||
}
|
||||
|
||||
type JWTVerifier interface {
|
||||
VerifyJWTSignatureAndClaims(jwt *JWT, token string) error
|
||||
}
|
||||
|
||||
type TraefikOidc struct {
|
||||
next http.Handler
|
||||
name string
|
||||
@@ -36,6 +44,8 @@ type TraefikOidc struct {
|
||||
httpClient *http.Client
|
||||
logger *Logger
|
||||
redirectURL string
|
||||
tokenVerifier TokenVerifier
|
||||
jwtVerifier JWTVerifier
|
||||
}
|
||||
|
||||
type ProviderMetadata struct {
|
||||
@@ -45,6 +55,59 @@ type ProviderMetadata struct {
|
||||
JWKSURL string `json:"jwks_uri"`
|
||||
}
|
||||
|
||||
func (t *TraefikOidc) VerifyToken(token string) error {
|
||||
t.logger.Debugf("Verifying token")
|
||||
if !t.limiter.Allow() {
|
||||
return fmt.Errorf("rate limit exceeded")
|
||||
}
|
||||
|
||||
if t.tokenBlacklist.IsBlacklisted(token) {
|
||||
return fmt.Errorf("token is blacklisted")
|
||||
}
|
||||
|
||||
if _, exists := t.tokenCache.Get(token); exists {
|
||||
t.logger.Debugf("Token is valid and cached")
|
||||
return nil // Token is valid and cached
|
||||
}
|
||||
|
||||
jwt, err := parseJWT(token)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse JWT: %w", err)
|
||||
}
|
||||
|
||||
if err := t.VerifyJWTSignatureAndClaims(jwt, token); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
expirationTime := time.Unix(int64(jwt.Claims["exp"].(float64)), 0)
|
||||
t.tokenCache.Set(token, expirationTime)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TraefikOidc) VerifyJWTSignatureAndClaims(jwt *JWT, token string) error {
|
||||
jwks, err := t.jwkCache.GetJWKS(t.jwksURL, t.httpClient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get JWKS: %w", err)
|
||||
}
|
||||
|
||||
kid, ok := jwt.Header["kid"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("missing key ID in token header")
|
||||
}
|
||||
|
||||
publicKeyPEM, err := getPublicKeyPEM(jwks, kid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := verifySignature(token, publicKeyPEM); err != nil {
|
||||
return fmt.Errorf("signature verification failed: %w", err)
|
||||
}
|
||||
|
||||
return jwt.Verify(t.issuerURL, t.clientID)
|
||||
}
|
||||
|
||||
func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error) {
|
||||
store := sessions.NewCookieStore([]byte(config.SessionEncryptionKey))
|
||||
store.Options = &sessions.Options{
|
||||
@@ -82,6 +145,9 @@ func New(ctx context.Context, next http.Handler, config *Config, name string) (h
|
||||
logger: NewLogger(config.LogLevel),
|
||||
redirectURL: "",
|
||||
}
|
||||
|
||||
t.tokenVerifier = t
|
||||
t.jwtVerifier = t
|
||||
t.startTokenCleanup()
|
||||
return t, nil
|
||||
}
|
||||
@@ -92,6 +158,9 @@ func discoverProviderMetadata(providerURL string, httpClient http.Client) (*Prov
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch provider metadata: %w", err)
|
||||
}
|
||||
if resp == nil {
|
||||
return nil, fmt.Errorf("received nil response from provider")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
@@ -150,32 +219,23 @@ func (t *TraefikOidc) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
func (t *TraefikOidc) determineScheme(req *http.Request) string {
|
||||
scheme := req.URL.Scheme
|
||||
if scheme == "" {
|
||||
scheme = req.Header.Get("X-Forwarded-Proto")
|
||||
}
|
||||
if scheme == "" {
|
||||
if req.TLS != nil {
|
||||
scheme = "https"
|
||||
} else {
|
||||
scheme = "http"
|
||||
}
|
||||
}
|
||||
if t.forceHTTPS {
|
||||
scheme = "https"
|
||||
return "https"
|
||||
}
|
||||
return scheme
|
||||
if scheme := req.Header.Get("X-Forwarded-Proto"); scheme != "" {
|
||||
return scheme
|
||||
}
|
||||
if req.TLS != nil {
|
||||
return "https"
|
||||
}
|
||||
return "http"
|
||||
}
|
||||
|
||||
func (t *TraefikOidc) determineHost(req *http.Request) string {
|
||||
host := req.URL.Host
|
||||
if host == "" {
|
||||
host = req.Header.Get("X-Forwarded-Host")
|
||||
if host := req.Header.Get("X-Forwarded-Host"); host != "" {
|
||||
return host
|
||||
}
|
||||
if host == "" {
|
||||
host = req.Host
|
||||
}
|
||||
return host
|
||||
return req.Host
|
||||
}
|
||||
|
||||
func (t *TraefikOidc) isUserAuthenticated(session *sessions.Session) bool {
|
||||
@@ -232,7 +292,7 @@ func (t *TraefikOidc) initiateAuthentication(rw http.ResponseWriter, req *http.R
|
||||
}
|
||||
|
||||
func (t *TraefikOidc) verifyToken(token string) error {
|
||||
return t.verifyAndCacheToken(token)
|
||||
return t.tokenVerifier.VerifyToken(token)
|
||||
}
|
||||
|
||||
func (t *TraefikOidc) buildAuthURL(redirectURL, state, nonce string) string {
|
||||
|
||||
Reference in New Issue
Block a user