Files
traefikoidc/internal/providers/azure.go
T

112 lines
3.4 KiB
Go

package providers
import (
"net/url"
"strings"
"time"
)
// AzureProvider encapsulates Azure AD-specific OIDC logic.
type AzureProvider struct {
*BaseProvider
}
// NewAzureProvider creates a new instance of the AzureProvider.
func NewAzureProvider() *AzureProvider {
return &AzureProvider{
BaseProvider: NewBaseProvider(),
}
}
// GetType returns the provider's type.
func (p *AzureProvider) GetType() ProviderType {
return ProviderTypeAzure
}
// GetCapabilities returns the specific capabilities of the Azure provider.
func (p *AzureProvider) GetCapabilities() ProviderCapabilities {
return ProviderCapabilities{
SupportsRefreshTokens: true,
RequiresOfflineAccessScope: true,
PreferredTokenValidation: "access", // Azure AD prefers access token validation
}
}
// BuildAuthParams configures Azure-specific authentication parameters.
func (p *AzureProvider) BuildAuthParams(baseParams url.Values, scopes []string) (*AuthParams, error) {
baseParams.Set("response_mode", "query")
// Ensure "offline_access" scope is present for refresh tokens
hasOfflineAccess := false
for _, scope := range scopes {
if scope == "offline_access" {
hasOfflineAccess = true
break
}
}
if !hasOfflineAccess {
scopes = append(scopes, "offline_access")
}
return &AuthParams{
URLValues: baseParams,
Scopes: scopes,
}, nil
}
// ValidateTokens overrides the default token validation to implement Azure-specific logic.
// Azure may use access tokens for validation, and this method ensures that behavior is preserved.
func (p *AzureProvider) ValidateTokens(session Session, verifier TokenVerifier, tokenCache TokenCache, refreshGracePeriod time.Duration) (*ValidationResult, error) {
if !session.GetAuthenticated() {
if session.GetRefreshToken() != "" {
return &ValidationResult{NeedsRefresh: true}, nil
}
return &ValidationResult{IsExpired: true}, nil
}
accessToken := session.GetAccessToken()
idToken := session.GetIDToken()
if accessToken != "" {
if strings.Count(accessToken, ".") == 2 {
if err := verifier.VerifyToken(accessToken); err != nil {
if idToken != "" {
return p.ValidateTokenExpiry(session, idToken, tokenCache, refreshGracePeriod)
}
if session.GetRefreshToken() != "" {
return &ValidationResult{NeedsRefresh: true}, nil
}
return &ValidationResult{IsExpired: true}, nil
}
return p.ValidateTokenExpiry(session, accessToken, tokenCache, refreshGracePeriod)
}
if idToken != "" {
return p.ValidateTokenExpiry(session, idToken, tokenCache, refreshGracePeriod)
}
return &ValidationResult{Authenticated: true}, nil
}
if idToken != "" {
if err := verifier.VerifyToken(idToken); err != nil {
if session.GetRefreshToken() != "" {
return &ValidationResult{NeedsRefresh: true}, nil
}
return &ValidationResult{IsExpired: true}, nil
}
return p.ValidateTokenExpiry(session, idToken, tokenCache, refreshGracePeriod)
}
if session.GetRefreshToken() != "" {
return &ValidationResult{NeedsRefresh: true}, nil
}
return &ValidationResult{IsExpired: true}, nil
}
// ValidateConfig validates Azure-specific configuration requirements.
// Azure requires specific tenant configuration and scope handling.
func (p *AzureProvider) ValidateConfig() error {
// Azure provider validation - ensure we have the necessary configuration
// In a real implementation, this might check for tenant ID, proper issuer URL format, etc.
return p.BaseProvider.ValidateConfig()
}