7. Pluggable Authentication Provider Pattern
Date: 2025-10-13Status
AcceptedCategory
Core ArchitectureContext
Enterprise applications require integration with existing identity providers (Keycloak, Okta, Auth0, Azure AD), while development and testing environments benefit from simpler authentication mechanisms. A hardcoded authentication approach creates several problems:- Enterprise Adoption Barrier: Cannot integrate with existing SSO infrastructure
- Development Friction: Requires full SSO setup for local development
- Testing Complexity: Integration tests require live identity provider
- Vendor Lock-in: Switching identity providers requires code changes
- Multi-Tenant Challenges: Different customers may require different auth providers
- Development: Simple username/password without external dependencies
- Enterprise: Keycloak SSO with OIDC/OAuth2, MFA, role synchronization
- Testing: Mock provider with predictable behavior
- Future: Okta, Auth0, Azure AD, custom LDAP integration
Decision
We will implement a pluggable authentication provider pattern using the Abstract Base Class (ABC) design pattern with multiple concrete implementations.Architecture
Factory Pattern
Configuration
Consequences
Positive Consequences
- Enterprise Ready: Seamless SSO integration with Keycloak, Okta, etc.
- Development Speed: No SSO setup required for local development
- Testability: Mock providers for fast, isolated unit tests
- Flexibility: Switch providers via configuration (no code changes)
- Extensibility: New providers (Okta, Auth0) add easily
- Multi-Tenant Support: Different customers can use different providers
Negative Consequences
- Complexity: Multiple implementations to maintain
- Feature Parity: Not all providers support all features (e.g., MFA)
- Testing Burden: Each provider requires separate test coverage
- Interface Leakage: Provider-specific features may not fit abstraction
Neutral Consequences
- Token Format Variance: JWT structure differs between providers
- Configuration Overhead: More environment variables to manage
Alternatives Considered
1. Keycloak Only (Direct Integration)
Pros: Simpler (single code path), full feature access Cons: Requires Keycloak for development, cannot test without it, vendor lock-in Why Rejected: Development friction and vendor lock-in outweigh simplicity2. Passport.js Strategy Pattern
Pros: Well-established pattern, many existing strategies Cons: JavaScript library (not Python), heavier abstraction Why Rejected: Not Python-native, over-engineered for our needs3. OAuth2/OIDC Generic Client
Pros: Works with any OIDC provider, standard protocol Cons: Does not support non-OIDC providers, complex configuration Why Rejected: Limits non-OIDC providers (e.g., simple API key auth)4. Hardcoded Multi-Provider Support
Pros: No abstraction overhead, direct provider access Cons: if/elif chains throughout codebase, hard to extend Why Rejected: Unmaintainable as provider count growsImplementation Details
InMemoryUserProvider
- In-memory user dictionary
- Simple password validation
- Instant response (no network calls)
- Pre-configured test users
KeycloakUserProvider
- OIDC/OAuth2 integration
- JWT verification with JWKS
- Automatic role synchronization to OpenFGA
- Token refresh support
- MFA support (via Keycloak)
JWT Verification (Keycloak)
- Public key verification (no shared secrets)
- JWKS caching (performance optimization)
- Signature verification (prevents tampering)
- Claims validation (audience, issuer, expiration)
Integration Points
AuthMiddleware (src/mcp_server_langgraph/auth/middleware.py):
- Accepts
user_provider: UserProviderparameter - Defaults to
InMemoryUserProvider()for backward compatibility - Calls provider methods: authenticate, get_user, refresh_token
src/mcp_server_langgraph/auth/keycloak.py:545):
Testing Strategy
Unit Tests (tests/test_user_provider.py):
- InMemoryUserProvider: 20/20 tests passing (100%)
- KeycloakUserProvider interface: 15/20 tests passing (needs Keycloak mock improvements)
- Factory function: 5/5 tests passing (100%)
- JWT verification: 10/10 tests passing (100%)
tests/test_keycloak.py):
- End-to-end authentication flow
- Token refresh
- Role synchronization
Migration Path
Existing deployments: No migration required (defaults to InMemory) Enabling Keycloak:- Deploy Keycloak instance
- Create realm and client
- Set
AUTH_PROVIDER=keycloak - Configure Keycloak settings
- Restart application
Future Enhancements
- Okta provider (OktaUserProvider)
- Auth0 provider (Auth0UserProvider)
- Azure AD provider (AzureADUserProvider)
- LDAP provider (LDAPUserProvider)
- API key provider (APIKeyUserProvider)
References
- Implementation:
src/mcp_server_langgraph/auth/user_provider.py:1-400 - Factory Pattern:
src/mcp_server_langgraph/auth/factory.py:1-188(recommended for production) - Keycloak Client:
src/mcp_server_langgraph/auth/keycloak.py:1-650 - Tests:
tests/test_user_provider.py,tests/test_keycloak.py,tests/test_auth_factory.py - AuthMiddleware:
src/mcp_server_langgraph/auth/middleware.py - Related ADRs: