mockapi/docs/TECH_SPEC_OAUTH2_CONTROLLERS.md
2026-03-16 10:49:01 +00:00

160 lines
No EOL
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Technical Specification: OAuth2 Controllers (Phase 6.4)
## Overview
This document provides the implementation blueprint for OAuth2 controllers in the Configurable Mock API application. The implementation follows the existing Repository-Service-Controller pattern and integrates with the admin interface.
## 1. File Structure
### New Files
- `oauth2/controller.py` OAuth2 standard endpoints (RFC 6749, 7662, 7009, OIDC)
- `oauth2/auth_code_store.py` Inmemory storage for authorization codes
- `templates/admin/oauth_clients.html` List OAuth clients
- `templates/admin/oauth_client_form.html` Create/edit client form
- `templates/admin/oauth_tokens.html` List OAuth tokens
- `templates/admin/oauth_users.html` List OAuth users (optional)
- `templates/oauth/authorize_consent.html` Authorization consent page
### Modified Files
- `controllers/admin_controller.py` Add admin OAuth2 management routes under `/admin/oauth`
- `config.py` Add OAuth2 configuration settings
- `app.py` Include OAuth2 router
## 2. OAuth2 Standard Endpoints
### Router: `/oauth`
| Endpoint | Method | Purpose |
|----------|--------|---------|
| `/oauth/authorize` | GET | Display consent screen |
| `/oauth/authorize` | POST | Process consent |
| `/oauth/token` | POST | Issue tokens (all grant types) |
| `/oauth/userinfo` | GET | Return user claims (OpenID Connect) |
| `/oauth/introspect` | POST | Token introspection (RFC 7662) |
| `/oauth/revoke` | POST | Token revocation (RFC 7009) |
| `/.well-known/openid-configuration` | GET | OIDC discovery metadata |
### Dependencies
- Database session: `Depends(get_db)`
- Token validation: `get_current_token_payload` (for userinfo)
- Client authentication: HTTP Basic for introspection/revocation
## 3. Admin OAuth2 Management Endpoints
### Router: `/admin/oauth`
| Endpoint | Method | Purpose |
|----------|--------|---------|
| `/admin/oauth/clients` | GET | List clients (paginated) |
| `/admin/oauth/clients/new` | GET | Create client form |
| `/admin/oauth/clients` | POST | Create client |
| `/admin/oauth/clients/{client_id}/edit` | GET | Edit client form |
| `/admin/oauth/clients/{client_id}` | POST | Update client |
| `/admin/oauth/clients/{client_id}/delete` | POST | Deactivate client |
| `/admin/oauth/tokens` | GET | List tokens with filters |
| `/admin/oauth/tokens/{token_id}/revoke` | POST | Revoke token |
| `/admin/oauth/users` | GET | List users (optional) |
### Authentication
- Protected by existing `AuthMiddleware` (sessionbased).
## 4. Configuration Additions (`config.py`)
```python
# Add to Settings class
oauth2_issuer: str = "http://localhost:8000"
oauth2_access_token_expire_minutes: int = 30
oauth2_refresh_token_expire_days: int = 7
oauth2_authorization_code_expire_minutes: int = 10
oauth2_supported_grant_types: List[str] = [
"authorization_code",
"client_credentials",
"refresh_token",
]
oauth2_supported_scopes: List[str] = [
"openid", "profile", "email", "api:read", "api:write"
]
```
## 5. Authorization Code Store
Create `oauth2/auth_code_store.py` with an inmemory dictionary protected by `asyncio.Lock`. Store authorization codes with expiration (datetime). Provide methods:
- `store_code(code: str, data: dict)`
- `get_code(code: str) -> Optional[dict]`
- `delete_code(code: str)`
## 6. Template Requirements
All admin templates extend `base.html` and use Bootstrap 5 styling.
- **oauth_clients.html**: Table with columns: Client ID, Name, Grant Types, Redirect URIs, Active, Actions.
- **oauth_client_form.html**: Form fields: client_id, client_secret (plaintext), name, redirect_uris (newlineseparated), grant_types (checkboxes), scopes (newlineseparated), is_active (checkbox).
- **oauth_tokens.html**: Table with columns: Access Token (truncated), Client, User, Scopes, Expires, Active, Revoke button.
- **authorize_consent.html**: Simple page showing client name, requested scopes, Allow/Deny buttons.
## 7. Integration with Existing Code
- Use existing `OAuthService`, `TokenService`, `ClientService`, `ScopeService`.
- Use `OAuthClientRepository`, `OAuthTokenRepository`, `OAuthUserRepository`.
- Update `app.py` to include OAuth2 router after admin router.
## 8. Security Considerations
- Validate redirect_uri exactly (including query parameters).
- Hash client secrets with bcrypt (already implemented).
- Implement token revocation by deletion from database.
- Use `state` parameter for CSRF protection in authorization flow.
- Log all authentication failures.
## 9. Implementation Steps for @coder
1. **Create authorization code store** (`oauth2/auth_code_store.py`).
2. **Implement OAuth2 controller** (`oauth2/controller.py`) with all endpoints.
3. **Extend admin controller** (`controllers/admin_controller.py`) with OAuth2 management routes.
4. **Create HTML templates** in `templates/admin/` and `templates/oauth/`.
5. **Update configuration** (`config.py`) with OAuth2 settings.
6. **Update app** (`app.py`) to include OAuth2 router.
7. **Test** with curl/Postman and verify admin pages.
## 10. Example Code Snippets
### OAuth2 Controller Example
```python
# oauth2/controller.py
@router.post("/token")
async def token_endpoint(
grant_type: str = Form(...),
client_id: Optional[str] = Form(None),
client_secret: Optional[str] = Form(None),
code: Optional[str] = Form(None),
redirect_uri: Optional[str] = Form(None),
refresh_token: Optional[str] = Form(None),
scope: Optional[str] = Form(None),
db: AsyncSession = Depends(get_db),
):
oauth_service = OAuthService(db)
if grant_type == "authorization_code":
# validate code, redirect_uri
pass
# ...
```
### Admin Controller Example
```python
# controllers/admin_controller.py
@router.get("/oauth/clients", response_class=HTMLResponse)
async def list_oauth_clients(
request: Request,
page: int = 1,
db: AsyncSession = Depends(get_db),
):
repo = OAuthClientRepository(db)
clients = await repo.get_all(skip=(page-1)*PAGE_SIZE, limit=PAGE_SIZE)
# render template
```
## 11. Next Steps (Phase 6.5)
- Update `PROJECT_PLAN.md` with completed items.
- Write integration tests for OAuth2 flows.
- Consider adding PKCE support (optional).
---
**Approval Required**: Please review this specification before implementation begins. Any changes should be documented in `PROJECT_PLAN.md`.