Branch: feat/auth-service-auth-provider-interface
Goal: Implement AuthProvider interface in SDK as infrastructure
We're creating the authentication abstraction layer in the SDK, following the same pattern as DatabaseConnector. This is infrastructure code that other services will consume.
graph TB
subgraph "SDK Infrastructure Layer"
A[AuthProvider Interface<br/>Abstract Base Class]
B[Auth Exceptions<br/>Custom Error Types]
end
subgraph "Future Implementations - Not in this branch"
C[FirebaseAuthProvider]
D[OAuthIntrospectionProvider]
E[LocalAuthProvider]
end
subgraph "Services - Consumer Layer"
F[auth_service Package<br/>Future]
end
C -.implements.-> A
D -.implements.-> A
E -.implements.-> A
F -.uses.-> A
F -.uses.-> B
style A fill:#e1f5ff
style B fill:#e1f5ff
style C fill:#f0f0f0
style D fill:#f0f0f0
style E fill:#f0f0f0
style F fill:#f0f0f0
This branch only creates the blue boxes - the interface and exceptions.
# Location: src/microcommerce_sdk/auth/interface.py
from abc import ABC, abstractmethod
from typing import Optional
class AuthProvider(ABC):
"""
Abstract authentication provider interface.
This interface defines the contract for authentication operations
that all providers must implement. Enables swappable auth strategies.
"""
@abstractmethod
async def authenticate(self, credentials: dict) -> tuple[dict, str]:
"""
Authenticate user with provided credentials.
Args:
credentials: Dict with auth data
For email/password: {"email": str, "password": str}
For token: {"token": str}
For OAuth: {"code": str, "redirect_uri": str}
Returns:
Tuple of (user_data, token)
- user_data: Dict with user info from auth system
Example: {"id": "auth_123", "email": "[email protected]", ...}
- token: JWT or session token string
Raises:
AuthenticationError: Invalid credentials
AuthProviderError: Auth system unavailable
Example:
user_data, token = await provider.authenticate({
"email": "[email protected]",
"password": "secret123"
})
"""
pass
@abstractmethod
async def validate_token(self, token: str) -> Optional[dict]:
"""
Validate authentication token and extract user data.
Args:
token: JWT or session token to validate
Returns:
User data dict if valid, None if invalid/expired
Example: {"id": "auth_123", "email": "[email protected]"}
Raises:
AuthProviderError: Auth system unavailable
Example:
user_data = await provider.validate_token("eyJhbG...")
if user_data:
print(f"Valid token for: {user_data['email']}")
"""
pass
@abstractmethod
async def refresh_token(self, refresh_token: str) -> str:
"""
Generate new access token from refresh token.
Args:
refresh_token: Valid refresh token
Returns:
New access token string
Raises:
AuthenticationError: Refresh token invalid/expired
AuthProviderError: Auth system unavailable
Example:
new_token = await provider.refresh_token(old_refresh_token)
"""
pass
@abstractmethod
async def revoke_token(self, token: str) -> bool:
"""
Revoke/invalidate an authentication token.
Args:
token: Token to revoke
Returns:
True if revoked successfully, False if already invalid
Raises:
AuthProviderError: Auth system unavailable
Example:
success = await provider.revoke_token(user_token)
"""
pass
@abstractmethod
async def create_user(self, user_data: dict) -> tuple[str, str]:
"""
Create new user in authentication system.
Args:
user_data: User registration data
Required: {"email": str, "password": str}
Optional: {"display_name": str, ...}
Returns:
Tuple of (auth_user_id, initial_token)
- auth_user_id: Unique ID in auth system
- initial_token: JWT/session token for immediate login
Raises:
ValidationError: User data invalid
UserExistsError: User already exists
AuthProviderError: Auth system unavailable
Example:
auth_id, token = await provider.create_user({
"email": "[email protected]",
"password": "secure123",
"display_name": "New User"
})
"""
passgraph TB
A[Exception<br/>Python built-in]
B[AuthProviderError<br/>Base auth exception]
C[AuthenticationError<br/>Invalid credentials]
D[TokenExpiredError<br/>Token expired]
E[UserExistsError<br/>Duplicate user]
F[ValidationError<br/>Invalid data]
A --> B
B --> C
B --> E
B --> F
C --> D
style A fill:#f0f0f0
style B fill:#ffe1e1
style C fill:#fff4e1
style D fill:#fff4e1
style E fill:#fff4e1
style F fill:#fff4e1
# Location: src/microcommerce_sdk/auth/exceptions.py
class AuthProviderError(Exception):
"""
Base exception for all auth provider errors.
Attributes:
message: Human-readable error description
provider: Name of the auth provider (optional)
"""
def __init__(self, message: str, provider: str = None):
self.message = message
self.provider = provider
super().__init__(self.message)
class AuthenticationError(AuthProviderError):
"""
Raised when authentication fails.
Common causes:
- Invalid email/password
- Invalid token
- Account disabled
"""
pass
class TokenExpiredError(AuthenticationError):
"""
Raised when token has expired.
Client should refresh token or re-authenticate.
"""
pass
class UserExistsError(AuthProviderError):
"""
Raised when attempting to create duplicate user.
Typically occurs when email already registered.
"""
pass
class ValidationError(AuthProviderError):
"""
Raised when user data fails validation.
Attributes:
validation_errors: List of specific validation failures
"""
def __init__(self, message: str, validation_errors: list[str] = None):
super().__init__(message)
self.validation_errors = validation_errors or []sequenceDiagram
participant Service as AuthService
participant Provider as AuthProvider
participant AuthSystem as Auth System
Service->>Provider: create_user(email, password)
Provider->>AuthSystem: Create user account
alt Success
AuthSystem-->>Provider: auth_user_id
Provider->>AuthSystem: Generate initial token
AuthSystem-->>Provider: token
Provider-->>Service: (auth_user_id, token)
else User Exists
AuthSystem-->>Provider: Error: Email exists
Provider-->>Service: Raise UserExistsError
else Invalid Data
AuthSystem-->>Provider: Error: Invalid format
Provider-->>Service: Raise ValidationError
end
sequenceDiagram
participant Service as AuthService
participant Provider as AuthProvider
participant AuthSystem as Auth System
Service->>Provider: authenticate(email, password)
Provider->>AuthSystem: Verify credentials
alt Valid Credentials
AuthSystem-->>Provider: User verified
Provider->>AuthSystem: Generate token
AuthSystem-->>Provider: token + user_data
Provider-->>Service: (user_data, token)
else Invalid Credentials
AuthSystem-->>Provider: Error: Invalid
Provider-->>Service: Raise AuthenticationError
end
sequenceDiagram
participant API as API Endpoint
participant Provider as AuthProvider
participant AuthSystem as Auth System
API->>Provider: validate_token(token)
Provider->>AuthSystem: Verify token signature
alt Valid Token
AuthSystem-->>Provider: Token valid + user_data
Provider-->>API: user_data dict
API->>API: Process request
else Expired Token
AuthSystem-->>Provider: Token expired
Provider-->>API: None
API->>API: Return 401 Unauthorized
else Invalid Token
AuthSystem-->>Provider: Invalid signature
Provider-->>API: None
API->>API: Return 401 Unauthorized
end
packages/microcommerce_sdk/src/microcommerce_sdk/auth/
├── __init__.py # Package exports
├── interface.py # AuthProvider abstract class
└── exceptions.py # Auth exceptions
"""
Authentication provider infrastructure for MicroCommerce SDK.
This module provides the abstract interface for authentication providers,
enabling swappable authentication strategies across different services.
See ADR-003 for design decisions and rationale.
"""
from .interface import AuthProvider
from .exceptions import (
AuthProviderError,
AuthenticationError,
TokenExpiredError,
UserExistsError,
ValidationError,
)
__all__ = [
# Interface
"AuthProvider",
# Exceptions
"AuthProviderError",
"AuthenticationError",
"TokenExpiredError",
"UserExistsError",
"ValidationError",
]-
Interface Compliance Tests
- Verify abstract methods exist
- Check method signatures
- Ensure proper inheritance
-
Exception Tests
- Exception hierarchy
- Exception attributes
- Error messages
-
Mock Provider Tests
- Create mock implementation for testing
- Verify all methods are called correctly
- Test error scenarios
packages/microcommerce_sdk/tests/
├── test_auth_provider_interface.py # Interface tests
└── test_auth_exceptions.py # Exception tests
We define the contract before any implementation. This ensures:
- All providers have consistent API
- Services can depend on stable interface
- Easy to add new providers later
All methods are async because authentication typically involves:
- Network calls to auth systems
- Database queries
- Token generation/validation
The interface doesn't assume any specific auth system:
- No Firebase-specific methods
- No OAuth-specific parameters
- Generic
credentialsdict for flexibility
AuthProvider handles:
- Authentication (verify credentials)
- Token lifecycle (create, validate, refresh, revoke)
- User creation in auth system
AuthProvider does NOT handle:
- User profile data (belongs to repositories)
- Business logic (belongs to services)
- Authorization/permissions (separate concern)
Out of scope for this branch:
- ❌ Firebase implementation (separate branch)
- ❌ OAuth implementation (separate branch)
- ❌ Service layer (auth_service package)
- ❌ User models (auth_service package)
- ❌ Integration tests (requires implementations)
Focus: Pure interface and exception definitions only.
This branch is complete when:
✅ AuthProvider interface defined with all 5 methods ✅ All exception classes defined ✅ Package exports configured ✅ Interface compliance tests passing ✅ Exception tests passing ✅ Documentation complete ✅ Follows ADR-003 design
After merging this branch to feat/auth-service-design:
-
Create
feat/auth-service-structure- Set up auth_service package
- Create ADR-001 for service architecture
-
Create
feat/auth-service-firebase-provider- Implement FirebaseAuthProvider
- First concrete implementation
-
Create
feat/auth-service-logic- AuthService and UserService
- Business logic layer
- Create
src/microcommerce_sdk/auth/directory - Create
interface.pywith AuthProvider class - Create
exceptions.pywith exception hierarchy - Create
__init__.pywith exports - Create
tests/test_auth_provider_interface.py - Create
tests/test_auth_exceptions.py - Run tests - all passing
- Update SDK
__init__.pyto export auth module - Commit with clear message
- Merge to parent branch
Ready to implement? Let me know and I'll create these files!