This directory contains the Flask-based frontend web application for the MyToDoApp project. The application provides a user-friendly interface for managing to-do items with AI-powered recommendations, Azure AD authentication, and integration with the backend Data API Builder service.
- Overview
- Architecture
- File Structure
- Core Application Files
- Supporting Modules
- Templates
- Static Assets
- Configuration
- Authentication Flow
- API Integration
- Local Development
- Related Documentation
The frontend web application is a Python Flask application that:
- Authenticates users via Azure Active Directory (Entra ID)
- Manages to-do items through GraphQL queries/mutations to the backend API
- Generates AI recommendations using Azure AI Foundry for task completion assistance
- Stores session data in Azure Redis Cache using Entra ID authentication
- Monitors telemetry through Azure Application Insights
- Runs in Azure Container Apps with managed identity for secure, passwordless authentication
- ✅ Zero-trust security: Managed identity authentication for all Azure services
- ✅ AI-powered recommendations: Integrated Azure AI Foundry for task suggestions
- ✅ Responsive UI: Bootstrap 5-based interface with dynamic content
- ✅ Session management: Redis-backed sessions with automatic token refresh
- ✅ Comprehensive logging: OpenTelemetry integration with Application Insights
- ✅ GraphQL integration: Full CRUD operations via Data API Builder backend
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Browser │─────▶│ Flask App │─────▶│ Data API │
│ (User UI) │ │ (Container) │ │ Builder │
└─────────────┘ └──────────────┘ └─────────────┘
│ │
├──────────────────────┤
▼ ▼
┌─────────────┐ ┌──────────┐
│ Redis │ │ Azure │
│ Cache │ │ SQL DB │
└─────────────┘ └──────────┘
│
▼
┌─────────────┐
│ Azure AD │
│ (Auth) │
└─────────────┘
│
▼
┌─────────────┐
│ Azure │
│ OpenAI │
└─────────────┘
The frontend communicates with:
- Azure AD: For user authentication and authorization
- Data API Builder: For CRUD operations on to-do items (see API Documentation)
- Azure Redis Cache: For session storage with Entra ID authentication
- Azure Key Vault: For secure credential storage (client secrets, connection strings)
- Azure AI Foundry: For generating task recommendations
- Application Insights: For telemetry and monitoring
Infrastructure details: Infrastructure Documentation
app/
├── app.py # Main Flask application with routes and business logic
├── dockerfile # Multi-stage container build configuration
├── requirements.txt # Python dependencies
├── context_processors.py # Flask template context injection (current date)
├── priority.py # Priority enumeration (HIGH, MEDIUM, LOW)
├── recommendation_engine.py # Azure AI Foundry integration for AI recommendations
├── services.py # Service enumeration (OpenAI, AzureOpenAI)
├── tab.py # Tab state enumeration (DETAILS, EDIT, RECOMMENDATIONS)
├── README.md # This documentation
├── static/ # Static assets (CSS, JS, images)
│ ├── css/
│ │ └── style.css # Custom application styles
│ ├── images/ # Image assets (favicon, etc.)
│ └── js/
│ └── app.js # Client-side JavaScript for UI interactions
└── templates/ # Jinja2 HTML templates
├── index.html # Main application interface
├── login.html # Login landing page
└── auth_error.html # Authentication error display
Purpose: Main Flask application containing all routes, business logic, authentication, and service integrations.
Key Components:
-
Configuration & Initialization (Lines 1-200)
- Environment variable loading (with optional
.envfor local development) - Managed identity credential setup (User-assigned MI when
AZURE_CLIENT_IDprovided, system-assigned otherwise) - Azure service client initialization:
- Key Vault for secrets retrieval
- Application Insights for telemetry
- Redis for session storage with Entra ID authentication
- MSAL confidential client for API access tokens
- Environment variable loading (with optional
-
Authentication Setup (Lines 200-250)
identity.web.Authconfiguration for user authentication- MSAL
ConfidentialClientApplicationfor app-to-API token acquisition - Token caching mechanism for API access tokens (
_get_api_access_token())
-
Health & Diagnostics (Lines 265-290)
/startupz: Startup probe endpoint for Container Apps health checks- Validates managed identity token acquisition for Redis
- Returns 200 when MI is ready, 503 during initialization
-
Session Management (Lines 290-400)
- Redis connection with Entra ID authentication using
redis-entraid - Flask-Session configuration for server-side session storage
- Automatic token refresh for Redis connections
- Redis connection with Entra ID authentication using
-
Application Routes:
-
/(index): Main application page- Loads user's to-do items from API
- Requires authentication (redirects to login if not authenticated)
-
/login: Azure AD login initiation- Triggers OAuth2 authorization code flow
-
/getAToken: OAuth2 callback endpoint- Receives authorization code from Azure AD
- Exchanges code for access token
- Establishes user session
-
/logout: Sign-out endpoint- Clears session
- Redirects to Azure AD logout
-
/add(POST): Create new to-do item- GraphQL mutation:
createtodo - Includes user OID (object ID) for data isolation
- GraphQL mutation:
-
/details/<id>: View to-do item details- GraphQL query: fetch single item by ID
- Displays in details panel
-
/edit/<id>: Edit to-do item form- Loads item data into edit panel
-
/update/<id>(POST): Update existing to-do item- GraphQL mutation:
updatetodo - Supports name, notes, priority, due date, completed status
- GraphQL mutation:
-
/remove/<id>: Delete to-do item- GraphQL mutation:
deletetodo
- GraphQL mutation:
-
/completed/<id>/<complete>: Toggle completion status- Quick toggle endpoint for checkbox interactions
-
/recommend/<id>: Generate AI recommendations- Calls
RecommendationEngine.get_recommendations() - Caches results in
recommendations_jsonfield - Supports refresh parameter to regenerate recommendations
- Calls
-
-
Helper Functions:
get_todo_by_id(): GraphQL query to fetch single to-do itemload_data_to_session(): Pre-request hook to load user's to-do listinject_common_variables(): Context processor for template variables
Environment Variables:
KEY_VAULT_NAME: Azure Key Vault name for secret retrievalAZURE_CLIENT_ID: User-assigned managed identity client IDREDIS_CONNECTION_STRING: Redis connection string (Entra ID format:rediss://identity@hostname:6380)IS_LOCALHOST: Set to"true"for local development modeAPI_APP_ID_URI: API application ID URI for token audience validationAPI_URL: Backend API endpoint (GraphQL endpoint)APPLICATIONINSIGHTS_CONNECTION_STRING: Application Insights telemetryREDIRECT_URI: OAuth2 redirect URI (optional, can be in Key Vault)
Authentication Flow:
- User accesses
/→ redirected to/login /loginredirects to Azure AD authorization endpoint- User authenticates with Azure AD
- Azure AD redirects to
/getATokenwith authorization code - App exchanges code for access token
- Session established, user redirected to
/
API Communication:
- Uses GraphQL POST requests to
API_URL - Authorization header:
Bearer <user-access-token> - Custom header:
X-MS-API-ROLE: MyToDoApp - Content-Type:
application/json
Purpose: Multi-stage container build configuration for the Flask application.
Build Strategy:
-
Base Image:
python:3.13-slim-bookworm(parameterized)- Slim variant minimizes image size
- Debian Bookworm for stability
-
Build Arguments:
PYTHON_VERSION=3.13: Python version (overridable)BASE_VARIANT=slim-bookworm: Base image variant
-
Optimization Techniques:
- Layer caching:
requirements.txtcopied first, installed separately - No bytecode:
PYTHONDONTWRITEBYTECODE=1prevents.pycfiles - Unbuffered output:
PYTHONUNBUFFERED=1for real-time logs - No cache:
--no-cache-dirreduces image size
- Layer caching:
-
Exposed Port: 80
- Matches Container App ingress configuration (see Infrastructure Docs)
-
Entrypoint:
python app.py- Starts Flask app directly
- App listens on port 80 in production, port 5000 in local mode
Container Runtime Behavior:
- When
IS_LOCALHOST=false(production): Listens on0.0.0.0:80 - When
IS_LOCALHOST=true(development): Listens onlocalhost:5000
Purpose: Python package dependencies for the Flask application.
Dependencies:
| Package | Version | Purpose |
|---|---|---|
openai |
Latest | Azure AI Foundry SDK for AI recommendations |
flask |
Latest | Web framework core |
flask[async] |
Latest | Async route support for /recommend |
azure-keyvault-secrets |
Latest | Key Vault secret retrieval |
azure-identity |
Latest | Managed identity authentication |
azure-monitor-opentelemetry |
Latest | Application Insights telemetry |
Flask-Session2 |
Latest | Server-side session management |
werkzeug |
>=2 | Flask HTTP utilities |
requests |
>=2,<3 | HTTP client for API calls |
identity |
>=0.5.1,<0.6 | Azure AD authentication wrapper |
msal |
>=1.26.0 | Microsoft Authentication Library (confidential client) |
redis |
Latest | Redis client for session storage |
redis-entraid |
>=0.2.0 | Entra ID authentication for Redis |
python-dotenv |
Latest | .env file support for local development |
Installation:
pip install --no-cache-dir -r requirements.txtPurpose: Azure AI Foundry integration for generating AI-powered task recommendations.
Class: RecommendationEngine
Authentication Strategy:
- Local mode (
IS_LOCALHOST=true): UsesDefaultAzureCredential(excluding MI) - Container mode: Uses
ManagedIdentityCredential(user-assigned whenAZURE_CLIENT_IDset)
Token Management:
- Acquires Entra ID tokens for Azure AI Foundry scope:
https://cognitiveservices.azure.com/.default - Automatic token refresh when expiry approaches (120-second buffer)
- Token caching to minimize authentication requests
Configuration:
- Deployment name: Retrieved from Key Vault secret
AZUREOPENAIDEPLOYMENTNAMEor env varAZURE_OPENAI_DEPLOYMENT_NAME - Endpoint: Retrieved from Key Vault secret
AZUREOPENAIENDPOINTor env varAZURE_OPENAI_ENDPOINT
Method: async get_recommendations(keyword_phrase, previous_links_str=None)
Parameters:
keyword_phrase(str): The to-do item name/descriptionprevious_links_str(str, optional): Comma-separated list of URLs to exclude from recommendations
Returns: List of recommendation dictionaries with title and link keys
Example:
engine = RecommendationEngine()
recommendations = await engine.get_recommendations("Buy a birthday gift for mom")
# Returns: [{"title": "...", "link": "..."}, ...]AI Prompt Strategy:
- System prompt: Defines bot as administrative assistant providing task completion resources
- User prompt: Requests 5 recommendations with title and hyperlink in JSON format
- Exclusion logic: Prevents duplicate recommendations when refreshing
API Configuration:
- Model: Deployment name from configuration (e.g.,
gpt-4o-mini,gpt-35-turbo) - API Version:
2024-02-15-preview - Temperature:
0.14(low randomness for consistent results) - Max tokens:
800 - Top P:
0.17(nucleus sampling)
Error Handling:
- JSON parsing failures return fallback message:
"Sorry, unable to recommendation at this time" - Token acquisition failures raise
RuntimeError
Purpose: Flask context processor to inject current date into all templates.
Function: inject_current_date()
Returns: Dictionary with current_date key containing today's date in YYYY-MM-DD format
Usage in Templates:
{% if todo.due_date < current_date %}
<small class="badge bg-danger">Past Due: {{ todo.due_date }}</small>
{% endif %}Registration: Automatically applied via @app.context_processor decorator in app.py
Purpose: Enumeration for to-do item priority levels.
Enum: Priority
Values:
NONE = 0: No priority assignedHIGH = 1: High priority (urgent/important)MEDIUM = 2: Medium priorityLOW = 3: Low priority
Usage:
from priority import Priority
session["PriorityEnum"] = PriorityTemplate Access:
{% if todo.priority == session.PriorityEnum.HIGH.value %}
<span class="badge bg-danger">High Priority</span>
{% endif %}Purpose: Enumeration for UI tab/panel states in the single-page application.
Enum: Tab
Values:
NONE = 0: No detail panel active (default list view)DETAILS = 1: Viewing to-do item detailsEDIT = 2: Editing to-do itemRECOMMENDATIONS = 3: Viewing AI recommendations
Usage in Routes:
from tab import Tab
session["selectedTab"] = Tab.RECOMMENDATIONSTemplate Logic:
{% if session["selectedTab"] == session.TabEnum.DETAILS %}
<!-- Display details panel -->
{% endif %}Purpose: Enumeration for AI service types (currently unused but reserved for future multi-provider support).
Enum: Service
Values:
OpenAI = "openai": Direct OpenAI APIAzureOpenAI = "azureopenai": Azure AI Foundry Service
Note: Current implementation uses only AzureOpenAI. This enum provides extensibility for future OpenAI API support.
Purpose: Main single-page application interface for authenticated users.
Structure:
-
Header Section:
- Page title:
<name>'s To-Do List - Sign Out link
- Page title:
-
Left Column (7/12 grid):
- To-Do List: Ordered list of all user's to-do items
- Checkbox for completion toggle
- Task name (clickable to show details)
- Due date badge (color-coded: past due = red, upcoming = blue, completed = green)
- Delete button (trash icon)
- Add Task Form: Input field + Add button at bottom
- To-Do List: Ordered list of all user's to-do items
-
Right Column (5/12 grid):
- Dynamic Detail Panel: Shows based on
session["selectedTab"]- DETAILS: Read-only view of selected to-do item
- Name, notes, priority, due date, completed status
- Edit and AI Recommend buttons
- EDIT: Edit form for selected to-do item
- Editable fields: name, notes, priority (dropdown), due date (date picker)
- Save and Cancel buttons
- RECOMMENDATIONS: AI-generated task suggestions
- List of clickable links with titles
- Refresh button to regenerate recommendations
- Stores recommendations in
recommendations_jsonfield
- DETAILS: Read-only view of selected to-do item
- Dynamic Detail Panel: Shows based on
JavaScript Dependencies:
app.js: Client-side interaction handlershandleClick(event, checkbox): Checkbox click handler with event propagation controlshowDetails(element): Navigate to details view for clicked item
Bootstrap Integration:
- Bootstrap 5.3.3 for responsive layout and components
- Custom styles from
static/css/style.css
Dynamic Content:
- Jinja2 templating with session data
- Color-coded badges based on due date comparison with
current_date - Conditional rendering based on
selectedTabstate
Purpose: Unauthenticated landing page with Azure AD sign-in link.
Content:
- Page title: "To-Do List"
- Sign In link: Redirects to
{{ auth_uri }}(Azure AD authorization endpoint)
Flow:
- User visits application root
/ @app.before_requestdecorator checksauth.get_user()- If not authenticated, redirects to
/login - User clicks "Sign In" → redirected to Azure AD
- After authentication, redirected to
/getATokencallback - Successfully authenticated users redirected to
/(index)
Purpose: Display authentication errors when Azure AD login fails.
Features:
- Password Reset Detection: Checks for error code
AADB2C90118(Azure AD B2C password reset)- Auto-redirects to password reset flow if configured
- Error Details: Displays error code and description from Azure AD response
- Homepage Link: Allows user to return to main page and retry
Variables:
result: Dictionary containingerroranderror_descriptionfrom Azure ADconfig: Flask configuration object (checks forB2C_RESET_PASSWORD_AUTHORITY)
Security: Flask automatically escapes unsafe input to prevent XSS
Purpose: Custom CSS styles for application-specific UI elements.
Styling Areas:
- Task list item styles
- Detail panel layouts
- Badge colors and spacing
- Button hover effects
- Form input styling
Purpose: Client-side JavaScript for interactive UI behaviors.
Key Functions:
-
handleClick(event, checkbox):- Handles to-do item completion checkbox clicks
- Prevents event bubbling to parent
<li>click handler - Sends AJAX request to
/completed/<id>/<complete>endpoint - Updates UI without full page reload
-
showDetails(element):- Handles click on to-do item to show details panel
- Extracts
data-idattribute from clicked element - Redirects to
/details/<id>route
AJAX Patterns:
- Uses
fetch()API for asynchronous requests - Updates DOM elements dynamically
- Provides responsive user experience without page reloads
Purpose: Image assets for the application.
Contents:
favicon.ico: Browser tab icon- Additional images as needed (logo, icons, etc.)
The application requires the following environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
KEY_VAULT_NAME |
Yes | - | Azure Key Vault name for secret retrieval |
AZURE_CLIENT_ID |
Recommended | - | User-assigned managed identity client ID |
REDIS_CONNECTION_STRING |
Yes | - | Redis connection string (Entra ID format) |
IS_LOCALHOST |
No | "false" |
Set to "true" for local development |
API_APP_ID_URI |
Yes | - | API application ID URI (e.g., api://guid) |
API_URL |
Yes | - | Backend API endpoint (e.g., https://api.../graphql/) |
APPLICATIONINSIGHTS_CONNECTION_STRING |
Yes | - | Application Insights connection string |
REDIRECT_URI |
No* | - | OAuth2 redirect URI (*can be in Key Vault as REDIRECT-URI secret) |
AUTHORITY |
No* | - | Azure AD authority URL (*can be in Key Vault) |
CLIENTID |
No* | - | Azure AD app registration client ID (*can be in Key Vault) |
CLIENTSECRET |
No* | - | Azure AD app registration client secret (*can be in Key Vault) |
Priority: Environment variables take precedence over Key Vault secrets.
When KEY_VAULT_NAME is configured, the application retrieves:
AUTHORITY: Azure AD authority URL (e.g.,https://login.microsoftonline.com/<tenant-id>)CLIENTID: Web app Azure AD application client IDCLIENTSECRET: Web app Azure AD application client secretREDIRECT-URI: OAuth2 redirect URI (e.g.,https://.../getAToken)AZUREOPENAIDEPLOYMENTNAME: Azure AI Foundry deployment name (e.g.,gpt-4o-mini)AZUREOPENAIENDPOINT: Azure AI Foundry endpoint URL
Access: Requires managed identity with Key Vault Secrets User role.
Entra ID Authentication:
rediss://identity-name@hostname:6380/0
Example:
rediss://todoapp-identity-abc123@todoapp-redis-abc123.redis.cache.windows.net:6380/0
Components:
rediss://: TLS-encrypted connectionidentity-name: Managed identity name (fromidentityNameparameter in infrastructure)hostname: Redis Cache hostname6380: Default port for TLS/0: Database number
┌─────────┐ ┌──────────┐ ┌──────────┐
│ Browser │ │ Flask App│ │ Azure AD │
└────┬────┘ └─────┬────┘ └─────┬────┘
│ │ │
│ 1. GET / │ │
├──────────────────────────▶│ │
│ │ │
│ 2. 302 Redirect /login │ │
│◀──────────────────────────┤ │
│ │ │
│ 3. GET /login │ │
├──────────────────────────▶│ │
│ │ │
│ 4. 302 Redirect to AD │ │
│◀──────────────────────────┤ │
│ │ │
│ 5. GET /authorize │ │
├───────────────────────────┼──────────────────────────▶│
│ │ │
│ 6. Show login form │ │
│◀──────────────────────────┼───────────────────────────┤
│ │ │
│ 7. POST credentials │ │
├───────────────────────────┼──────────────────────────▶│
│ │ │
│ 8. 302 with auth code │ │
│◀──────────────────────────┼───────────────────────────┤
│ │ │
│ 9. GET /getAToken?code=..│ │
├──────────────────────────▶│ │
│ │ │
│ │ 10. Exchange code for token
│ ├──────────────────────────▶│
│ │ │
│ │ 11. Return access token │
│ │◀──────────────────────────┤
│ │ │
│ 12. 302 Redirect to / │ │
│◀──────────────────────────┤ │
│ │ │
│ 13. GET / (authenticated)│ │
├──────────────────────────▶│ │
│ │ │
│ 14. Return index.html │ │
│◀──────────────────────────┤ │
┌──────────┐ ┌──────────┐ ┌──────────┐
│Flask App │ │ Azure AD │ │ Data API │
└─────┬────┘ └─────┬────┘ └─────┬────┘
│ │ │
│ 1. Request app token │ │
│ (client_id + secret) │ │
├──────────────────────────▶│ │
│ │ │
│ 2. Return access token │ │
│◀──────────────────────────┤ │
│ │ │
│ 3. GraphQL query │ │
│ + Bearer token │ │
├───────────────────────────┼──────────────────────────▶│
│ │ │
│ 4. Validate token │ │
│ │◀──────────────────────────┤
│ │ │
│ 5. Return data │ │
│◀──────────────────────────┼───────────────────────────┤
Token Caching: App-to-API tokens cached for duration of expires_in (minus 60-second buffer).
Endpoint: Value of API_URL environment variable (e.g., https://todoapp-api-abc123.azurecontainerapps.io/graphql/)
Authentication:
- Authorization Header:
Bearer <app-access-token>(from MSAL confidential client) - Custom Header:
X-MS-API-ROLE: MyToDoApp
Request Format:
{
"query": "mutation { ... }",
"variables": { "name": "...", "oid": "..." }
}1. Query All User's To-Dos:
{
todos(filter: { oid: { eq: "<user-oid>" } }) {
items {
id
name
recommendations_json
notes
priority
completed
due_date
oid
}
}
}2. Query Single To-Do by ID:
{
todo_by_pk(id: <id>) {
id
name
recommendations_json
notes
priority
completed
due_date
oid
}
}3. Create To-Do:
mutation Createtodo($name: String!, $oid: String!) {
createtodo(item: {name: $name, oid: $oid}) {
id
name
recommendations_json
notes
priority
completed
due_date
oid
}
}4. Update To-Do:
mutation Updatetodo($id: Int!, $name: String, $notes: String, $priority: Int, $due_date: String, $completed: Boolean, $recommendations_json: String) {
updatetodo(id: $id, item: {
name: $name
notes: $notes
priority: $priority
due_date: $due_date
completed: $completed
recommendations_json: $recommendations_json
}) {
id
name
recommendations_json
notes
priority
completed
due_date
oid
}
}5. Delete To-Do:
mutation Deletetodo($id: Int!) {
deletetodo(id: $id) {
id
}
}All queries filter by user's oid (Azure AD object ID) to ensure users only access their own data:
oid = auth.get_user().get("oid")
query = f'{{ todos(filter: {{ oid: {{ eq: "{oid}" }} }}) {{ items {{ ... }} }} }}'Backend Enforcement: Data API Builder validates JWT tokens and enforces role-based access control (see API Documentation).
- Python 3.13+: Install from python.org
- Azure CLI: Install from docs.microsoft.com/cli/azure/install-azure-cli
- Azure Subscription: With deployed infrastructure (see Infrastructure Setup)
- Environment Variables: Configured in
.envfile (see below)
-
Clone repository:
git clone <repo-url> cd MyToDoApp/app
-
Create virtual environment:
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate
-
Install dependencies:
pip install -r requirements.txt
-
Login to Azure:
az login
-
Create
.envfile inapp/directory:IS_LOCALHOST=true KEY_VAULT_NAME=todoapp-kv-abc123def456 API_URL=https://todoapp-api-abc123.azurecontainerapps.io/graphql/ API_APP_ID_URI=api://12345678-1234-1234-1234-123456789abc APPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=... REDIS_CONNECTION_STRING=rediss://todoapp-identity-abc123@todoapp-redis-abc123.redis.cache.windows.net:6380/0
Note: Omit
AUTHORITY,CLIENTID,CLIENTSECRET,REDIRECT_URI— they will be retrieved from Key Vault. -
Run application:
python app.py
-
Access application:
- Open browser:
http://localhost:5000 - Click "Sign In" to authenticate with Azure AD
- Open browser:
When IS_LOCALHOST=true:
- Uses
DefaultAzureCredential(authenticated Azure CLI user) - Listens on
localhost:5000(not0.0.0.0:80) - Enables Flask debug mode (
debug=True) - Optionally loads
.envfile viapython-dotenv
- Ensure Key Vault has
REDIRECT-URIsecret set tohttp://localhost:5000/getAToken - Or set
REDIRECT_URI=http://localhost:5000/getATokenin.env
- Verify
CLIENTIDandCLIENTSECRETin Key Vault - Check Azure AD app registration has API permission for
API_APP_ID_URI
- Ensure Azure CLI user has "Redis Cache Contributor" role on Redis Cache
- Verify
REDIS_CONNECTION_STRINGformat is correct (Entra ID format)
- Ensure Azure CLI user has "Cognitive Services User" role on AI Services resource
- Verify
AZUREOPENAIDEPLOYMENTNAMEandAZUREOPENAIENDPOINTin Key Vault
- Add
API_URLto.envfile - Ensure API is deployed and accessible (see API Documentation)
-
Infrastructure Documentation: Bicep modules, resource configuration, deployment process
- Container Apps Module: Frontend and API container configuration
- Authentication Module: Azure AD app registrations
- Redis Module: Redis Cache with Entra ID authentication
- Key Vault Module: Secret storage configuration
-
API Documentation: Data API Builder configuration, GraphQL schema, authentication
- Dockerfile: API container build process
- Entrypoint Script: Managed identity token wait logic
- DAB Configuration: GraphQL/REST API settings
- Troubleshooting: Common API issues and solutions
-
Scripts Documentation: azd lifecycle scripts
- preup.ps1: Pre-deployment setup (AD apps, AI Services, model selection)
- postprovision.ps1: Database configuration
- postdeploy.ps1: Redirect URI updates
- postup.ps1: Display connection information
- Flask Documentation
- Azure Identity SDK for Python
- Azure AI Foundry Service
- Azure Redis Cache with Entra ID
- Azure Container Apps
- Microsoft Authentication Library (MSAL) for Python
The frontend application is deployed to Azure Container Apps via azd deploy. See the Infrastructure Documentation for deployment details.
Container Configuration:
- Image: Built from
dockerfilein this directory - Port: 80 (matches Container App ingress
targetPort) - Environment Variables: Injected by Container App configuration (see infra/modules/aca.bicep)
- Health Probe:
/startupzendpoint (validates managed identity readiness) - Scaling: 0-3 replicas based on HTTP request concurrency
Deployment Command:
cd /workspaces/MyToDoApp
azd deploy appPost-Deployment:
- Frontend URL:
https://todoapp-app-<resourceToken>.azurecontainerapps.io - Redirect URI automatically updated by postdeploy.ps1
- Managed Identity: All Azure service authentication uses managed identity (no passwords/keys)
- Key Vault: Sensitive credentials stored securely, accessed via MI
- Redis Entra ID: Session storage uses Entra ID authentication (no Redis password)
- Application Insights: Telemetry uses Entra ID authentication via managed identity credential
- Azure AD: User authentication with OAuth2/OpenID Connect
- API Token Validation: Backend validates JWT tokens for every request
The application uses User-Assigned Managed Identity (specified via AZURE_CLIENT_ID) to authenticate to all Azure services, eliminating the need for passwords, connection strings with secrets, or API keys. This provides a passwordless, zero-trust security model.
Services Accessed via Managed Identity:
| Service | Authentication Method | Implementation | Purpose |
|---|---|---|---|
| Azure Key Vault | Managed Identity | DefaultAzureCredential |
Retrieves secrets (OAuth client secret, redirect URI) |
| Azure Redis Cache | Entra ID (via MI) | redis-entraid library with IdentityProvider |
Session storage with token-based authentication |
| Azure AI Foundry | Entra ID Token (via MI) | DefaultAzureCredential → get_token() for scope https://cognitiveservices.azure.com/.default |
AI recommendations with bearer token auth |
| Application Insights | Entra ID (via MI) | configure_azure_monitor(credential=managed_identity_credential) |
Telemetry and monitoring with managed identity |
Implementation Details:
# Single credential instance used across all services
managed_identity_credential = ManagedIdentityCredential(client_id=azure_client_id)
# Key Vault access
secret_client = SecretClient(vault_url=key_vault_url, credential=managed_identity_credential)
# Redis with Entra ID authentication
redis_client = redis.Redis.from_url(
redis_connection_string,
credential_provider=IdentityProvider(credential=managed_identity_credential)
)
# Azure AI Foundry token acquisition
openai_token = managed_identity_credential.get_token("https://cognitiveservices.azure.com/.default")
# Application Insights telemetry
configure_azure_monitor(
connection_string=app_insights_connection_string,
credential=managed_identity_credential
)Benefits:
- ✅ No secrets stored in code or environment variables (except connection string endpoints)
- ✅ Automatic token rotation and expiry handling
- ✅ Centralized access control via Azure RBAC
- ✅ Audit trail through Azure AD sign-in logs
- ✅ Works seamlessly in both local development (Azure CLI auth) and production (Container App MI)
Required Azure RBAC Roles (assigned to managed identity):
Key Vault Secrets Useron Key VaultRedis Cache Contributoror custom role withMicrosoft.Cache/redis/accessKeys/readon RedisCognitive Services OpenAI Useron Azure AI Foundry serviceMonitoring Metrics Publisheron Application Insights (automatically granted)
- User data filtered by Azure AD
oid(object ID) - Backend enforces role-based access control
- Session data stored server-side (not in cookies)
- All ingress is HTTPS (enforced by Container Apps)
- Redis connections use TLS (
rediss://) - API calls use HTTPS
- User tokens stored in server-side session (Redis)
- API access tokens cached with expiry tracking
- Automatic token refresh for long-running operations
When modifying the frontend application:
- Update this README if adding new routes, features, or configuration
- Test locally with
IS_LOCALHOST=truebefore deploying - Verify GraphQL queries match Data API Builder entity definitions (see API Documentation)
- Check Container App logs in Azure Portal after deployment
- Update infrastructure if new environment variables or secrets required (see Infrastructure Documentation)
For issues or questions:
- Check Troubleshooting section
- Review API Documentation for backend issues
- Consult Infrastructure Documentation for deployment problems
- Check Azure Portal logs for Container App, Redis, Key Vault, Application Insights
Last Updated: November 5, 2025
Version: 1.0
Maintainer: MyToDoApp Team