Skip to content

Azure Authentication Guide

Complete guide to Azure authentication across all deployment stages: Local Development, Docker Testing, and Azure Production.


Overview

Loan Defenders uses Azure AI Services for LLM capabilities. Authentication varies by deployment stage:

Stage Method Security Setup Time
Local Development az login ⭐⭐⭐⭐⭐ 1 minute
Docker Testing Service Principal ⭐⭐⭐⭐ 5 minutes
Azure Production Managed Identity ⭐⭐⭐⭐⭐ Automatic

Key Concept: DefaultAzureCredential automatically selects the right authentication method for each environment!

from azure.identity import DefaultAzureCredential

# Works everywhere with proper setup
credential = DefaultAzureCredential()

Local Development

🎯 Goal: Fastest, most secure local development
⏱️ Setup: 1 minute
🔐 Security: ⭐⭐⭐⭐⭐ (No secrets in files!)

How It Works

Use your Azure identity for authentication:

# One-time login
az login

# Your credentials stored securely by Azure CLI
# Application uses DefaultAzureCredential → finds az login credentials

Benefits: - ✅ No secrets in files - ✅ Uses your Azure permissions - ✅ Multi-factor authentication support - ✅ Automatic credential refresh - ✅ Audit trail per developer - ✅ Centralized access control

Setup Instructions

1. Install Azure CLI

# macOS
brew install azure-cli

# Ubuntu/Debian
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash

# Windows
winget install Microsoft.AzureCLI

2. Login to Azure

# Login (opens browser)
az login

# Verify you're logged in
az account show

# List available subscriptions
az account list --output table

# Set subscription (if multiple)
az account set --subscription "Your Subscription Name"

3. Verify Permissions

Ensure you have "Cognitive Services OpenAI User" role:

# Check your role assignments
az role assignment list --assignee $(az ad signed-in-user show --query id -o tsv) \
  --query "[?roleDefinitionName=='Cognitive Services OpenAI User']" \
  --output table

If not assigned, ask your admin to grant access.

4. Configure .env

cp .env.example .env

Edit .env - only these fields needed:

# Azure AI Configuration (REQUIRED)
AZURE_AI_PROJECT_ENDPOINT=https://your-resource.services.ai.azure.com/api/projects/your-project
AZURE_AI_MODEL_DEPLOYMENT_NAME=your-deployment-name

# Authentication (NOT NEEDED - az login handles it!)
# AZURE_TENANT_ID=           # Leave blank
# AZURE_CLIENT_ID=           # Leave blank
# AZURE_CLIENT_SECRET=       # Leave blank

5. Run Application

# Install dependencies
uv sync --prerelease=allow

# Start API
cd apps/api
uv run python -m loan_defenders.api.app

# Authentication automatic via az login!

Troubleshooting

Error: "DefaultAzureCredential failed to retrieve a token"

# Re-login
az login

# Verify
az account show

# Check if subscription is set
az account list --output table

Error: "Access denied" or "403 Forbidden"

# Check your role assignments
az role assignment list --assignee $(az ad signed-in-user show --query id -o tsv)

# Ask admin to grant "Cognitive Services OpenAI User" role

Docker Testing

🎯 Goal: Test full containerized stack locally
⏱️ Setup: 5 minutes
🔐 Security: ⭐⭐⭐⭐ (Secrets in .env, git-ignored)

Why Service Principal?

Docker containers cannot use az login (no browser access). Solution: Service Principal with explicit credentials.

How It Works

Service Principal (App Registration)
  ├── Client ID (public)
  ├── Client Secret (private, like a password)
  └── Tenant ID (public)

→ Stored in .env (git-ignored)
→ Docker containers use these for authentication
→ DefaultAzureCredential finds them automatically

Setup Instructions

Step 1: Create Service Principal

# Create Service Principal with required role
az ad sp create-for-rbac \
  --name "loan-defenders-docker-$(whoami)" \
  --role "Cognitive Services OpenAI User" \
  --scopes /subscriptions/YOUR_SUBSCRIPTION_ID

# Output (save this!):
{
  "appId": "12345678-1234-1234-1234-123456789012",         # → AZURE_CLIENT_ID
  "password": "abc-DEF_123~xyz",                           # → AZURE_CLIENT_SECRET
  "tenant": "87654321-4321-4321-4321-210987654321"         # → AZURE_TENANT_ID
}

Step 2: Add to .env

# Edit .env and add these lines:
AZURE_TENANT_ID=87654321-4321-4321-4321-210987654321
AZURE_CLIENT_ID=12345678-1234-1234-1234-123456789012
AZURE_CLIENT_SECRET=abc-DEF_123~xyz

# Keep existing:
AZURE_AI_PROJECT_ENDPOINT=https://your-resource.services.ai.azure.com/api/projects/your-project
AZURE_AI_MODEL_DEPLOYMENT_NAME=your-deployment-name

Step 3: Verify .env Security

# Ensure .env is git-ignored
cat .gitignore | grep ".env"

# Should see: .env

Run Docker Compose

# Build images
docker-compose build

# Start services
docker-compose up -d

# Check logs
docker-compose logs -f api

# Verify authentication working
docker-compose exec api curl http://localhost:8000/health

Security Best Practices

DO: - Store secrets in .env (git-ignored) - Rotate secrets every 90 days - Use unique Service Principal per developer - Delete Service Principal when leaving team

DON'T: - Commit .env to git - Share Service Principal secrets - Use production credentials for testing - Store secrets in plaintext files

Secret Rotation

# Rotate secret every 90 days
az ad sp credential reset --id YOUR_CLIENT_ID

# Update .env with new secret
# Restart containers
docker-compose restart

Azure Production

🎯 Goal: Production deployment with best security
⏱️ Setup: Automatic
🔐 Security: ⭐⭐⭐⭐⭐ (No secrets at all!)

How It Works

Managed Identity: Azure automatically provides identity to your Container Apps.

Container App
  ├── System-Assigned Managed Identity (automatic)
  └── RBAC: "Cognitive Services OpenAI User"

→ No secrets needed
→ No configuration needed
→ Azure handles everything automatically

Benefits

No secrets to manage - Azure handles credentials
Automatic rotation - No manual updates
Best security - No credentials can be stolen
RBAC integration - Centralized access control
Audit trail - All access logged automatically

Setup (Automated via Bicep)

When you deploy infrastructure, Bicep automatically:

  1. Creates Managed Identity for Container App
  2. Assigns "Cognitive Services OpenAI User" role
  3. Configures Container App to use identity
  4. No manual configuration needed!
// Bicep handles this automatically
resource containerApp 'Microsoft.App/containerApps@2024-03-01' = {
  identity: {
    type: 'SystemAssigned'  // ← Managed Identity created
  }
}

resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  properties: {
    principalId: containerApp.identity.principalId  // ← Identity assigned role
    roleDefinitionId: cognitiveServicesOpenAIUser   // ← "Cognitive Services OpenAI User"
  }
}

Application Code

No changes needed! Same code works everywhere:

from azure.identity import DefaultAzureCredential

# In Azure: Uses Managed Identity automatically
# In Docker: Uses Service Principal from env vars
# Locally: Uses az login credentials
credential = DefaultAzureCredential()

client = AzureOpenAIClient(
    endpoint=endpoint,
    credential=credential  # ← Works everywhere!
)

Verification

After deployment:

# Check Container App has identity
az containerapp show \
  --name loan-defenders-api \
  --resource-group loan-defenders-prod-rg \
  --query "identity"

# Check role assignment
az role assignment list \
  --assignee $(az containerapp show ... --query "identity.principalId" -o tsv) \
  --query "[?roleDefinitionName=='Cognitive Services OpenAI User']"

DefaultAzureCredential Authentication Chain

DefaultAzureCredential tries methods in this order:

1. Environment Variables
   ├── AZURE_CLIENT_ID
   ├── AZURE_CLIENT_SECRET
   └── AZURE_TENANT_ID

2. Managed Identity (in Azure)
   └── System-Assigned or User-Assigned

3. Azure CLI
   └── az login credentials

4. Visual Studio Code
   └── VS Code Azure extension

5. Azure PowerShell
   └── Connect-AzAccount

6. Interactive Browser
   └── Last resort - opens browser

This is why one code path works everywhere! 🎯


Comparison Matrix

Feature Local Dev Docker Test Azure Prod
Method az login Service Principal Managed Identity
Setup Time 1 min 5 min Automatic
Secrets in Files ❌ No ⚠️ Yes (.env) ❌ No
Browser Required ✅ Once ❌ No ❌ No
Rotation Needed ❌ No ⚠️ Every 90 days ❌ No
Works in Containers ❌ No ✅ Yes ✅ Yes
Production Ready ❌ No ❌ No ✅ Yes
Security Score ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐

Troubleshooting

"DefaultAzureCredential failed to retrieve a token"

Check which environment:

Local Development:

# Re-login
az login
az account show

Docker Testing:

# Check .env has credentials
grep AZURE_ .env

# Should see:
# AZURE_TENANT_ID=...
# AZURE_CLIENT_ID=...
# AZURE_CLIENT_SECRET=...

Azure Production:

# Check Managed Identity exists
az containerapp show --name api --resource-group rg --query "identity"

# Check role assignment
az role assignment list --assignee <identity-principal-id>

"Access Denied" or "403 Forbidden"

Local Development:

# Check your role
az role assignment list \
  --assignee $(az ad signed-in-user show --query id -o tsv) \
  --query "[?roleDefinitionName=='Cognitive Services OpenAI User']"

Docker/Azure:

# Verify Service Principal / Managed Identity has role
az role assignment list \
  --assignee <client-id or principal-id> \
  --query "[?roleDefinitionName=='Cognitive Services OpenAI User']"

Secrets Exposed in Git

If you accidentally committed .env:

# Remove from git
git rm --cached .env
echo ".env" >> .gitignore
git commit -m "Remove .env from tracking"

# IMPORTANT: Rotate all secrets!
az ad sp credential reset --id YOUR_CLIENT_ID

References


Quick Start: - Developing locally? → Run az login and you're done! - Testing Docker? → Use # See manual setup below - Deploying to Azure? → Managed Identity configured automatically!