Run automated checks to confirm secrets live in Key Vault, Managed Identity has the right RBAC, tokens expire, and no credentials leak into environment variables.

Camp 1 · Validate

Validate Security

Run automated checks, exercise manual verification steps, and review the full before/after security posture before moving on to Camp 2.

You've migrated secrets to Key Vault, enabled Managed Identity, and replaced the static token with OAuth 2.1 + JWT validation. Now it's time to prove all those security controls are actually in place. This waypoint runs an automated verification, walks through optional manual checks, and compares your secure server against the original vulnerable deployment.

Waypoint 6: Validate Security

Comprehensive Security Validation

Let's verify all security controls are properly configured:

cd camps/camp1-identity
./scripts/verify-security.sh
cd camps/camp1-identity
./scripts/verify-security.ps1

This script performs comprehensive checks:

Expected output:

Camp 1: Security Validation
==============================
Loading azd environment...

Running security checks...

Check 1: Secrets in Key Vault
------------------------------
Found 2 secrets in Key Vault
Name                        Enabled
--------------------------  ---------
demo-api-key               True
external-service-secret    True

Check 2: Managed Identity RBAC
-------------------------------
Managed Identity has Key Vault Secrets User role
Role                        Scope
--------------------------  --------------------------------------------------
Key Vault Secrets User      /subscriptions/.../resourceGroups/.../providers/...

Check 3: Container App Identity
--------------------------------
Checking if container apps have managed identity assigned...
Name                        Identity
--------------------------  -----------
ca-sherpa-camp1-xxxxx      UserAssigned

==============================
Security Validation Complete!
==============================

Verified:
  - Secrets stored in Key Vault (not env vars)
  - Managed Identity has RBAC permissions
  - Container Apps use Managed Identity

Security posture: SECURE
   Ready for production!

Manual Verification Steps (Optional - Extra Credit)

Extra Credit - Not Required

The automated script above validates all the essential security controls. The steps below are optional and provide hands-on experience with testing authentication and authorization failures. Great for deeper learning, but feel free to skip ahead to the Security Checklist!

Verify Token Expiration

Try using an old/expired token:

# This should FAIL with "Token expired" or "Invalid token"
curl -X POST ${SECURE_URL}/mcp \
-H "Authorization: Bearer expired_or_old_token" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
# This should FAIL with "Token expired" or "Invalid token"
curl.exe -X POST "$SECURE_URL/mcp" `
-H "Authorization: Bearer expired_or_old_token" `
-H "Content-Type: application/json" `
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'

Expected: 401 Unauthorized or similar error

Verify Audience Validation

Try using a token with wrong audience:

# Get a token for a different resource (e.g., Microsoft Graph)
WRONG_TOKEN=$(az account get-access-token --resource https://graph.microsoft.com --query accessToken -o tsv)

# This should FAIL because audience is wrong
curl -X POST ${SECURE_URL}/mcp \
-H "Authorization: Bearer $WRONG_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
# Get a token for a different resource (e.g., Microsoft Graph)
$WRONG_TOKEN = az account get-access-token --resource https://graph.microsoft.com --query accessToken -o tsv

# This should FAIL because audience is wrong
curl.exe -X POST "$SECURE_URL/mcp" `
-H "Authorization: Bearer $WRONG_TOKEN" `
-H "Content-Type: application/json" `
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'

Expected: 401 Unauthorized - audience validation failed

Verify No Secrets in Environment Variables
  1. Open Azure Portal
  2. Navigate to your secure Container App
  3. Go to SettingsEnvironment variables
  4. Verify: No REQUIRED_TOKEN variable!
  5. Only configuration: AZURE_CLIENT_ID, AZURE_TENANT_ID, KEY_VAULT_URL

Expected: No secret values visible, only configuration references


Security Checklist

Review what we've accomplished:

  • No hardcoded secrets in code
  • No secrets in environment variables (moved to Key Vault)
  • Managed Identity for Azure resource access (no passwords)
  • OAuth 2.1 authentication with Entra ID
  • JWT validation (signature, issuer, audience, expiration)
  • Least-privilege RBAC (Key Vault Secrets User only)
  • Audit logs enabled (Azure Monitor tracks all access)
  • Token expiration (tokens expire after ~1 hour)
  • Audience validation (prevents confused deputy attacks)

Compare: Before vs. After

Security Control Vulnerable Server Secure Server
Authentication Static token (camp1_demo_token_INSECURE) OAuth 2.1 JWT with Entra ID
Token Storage Hardcoded in env var (visible in Portal) Not applicable - JWT per request
Token Expiration Never ~1 hour
Token Revocation Impossible Possible via Entra ID
Token Tampering Possible (plain string) Cryptographically prevented (signed JWT)
Audience Validation No - token works for any service Yes - aud claim prevents confused deputy
User Context Generic client_id only Rich claims (name, email, roles, tenant)
Token Rotation Manual, risky Automatic via token refresh
Client Discovery Manual configuration PRM (RFC 9728) enables zero-config
Azure Credentials Connection strings in env vars Managed Identity (passwordless)
Secrets Management Environment variables Azure Key Vault
RBAC Not applicable Least-privilege (Key Vault Secrets User)
Audit Logs None Azure Monitor tracks all access
Production Ready Security vulnerabilities Enterprise-grade security

Summit View: What We Fixed

Vulnerability Solution OWASP Risk Mitigated
Hardcoded tokens OAuth 2.1 with Entra ID MCP01 (Token Mismanagement & Secret Exposure), MCP07 (Insufficient Authentication & Authorization)
Tokens never expire JWT with expiration (~1 hour) MCP01 (Token Mismanagement & Secret Exposure)
Secrets in env vars Azure Key Vault MCP01 (Token Mismanagement & Secret Exposure)
No audience validation JWTVerifier with aud check MCP07 (Insufficient Authentication & Authorization)
Password-based auth Managed Identity MCP01 (Token Mismanagement & Secret Exposure), MCP02 (Privilege Escalation via Scope Creep)
Over-privileged access Least-privilege RBAC MCP02 (Privilege Escalation via Scope Creep)

Cleanup

When you're done with Camp 1, remove all Azure resources:

# Delete all resources
azd down --force --purge

Optional: Delete the Entra ID application:

# Get app ID
APP_ID=$(azd env get-value AZURE_CLIENT_ID)

# Delete app
az ad app delete --id $APP_ID
# Get app ID
$APP_ID = azd env get-value AZURE_CLIENT_ID

# Delete app
az ad app delete --id $APP_ID
Summit route Identity ridge reached 2/5 camps
Current camp Identity camp cleared Complete