Claude-skill-registry azure-ad-sso
Azure AD OAuth2/OIDC SSO integration for Kubernetes applications. Use when implementing Single Sign-On, configuring Azure AD App Registrations, restricting access by groups, or integrating tools (DefectDojo, Grafana, ArgoCD, Harbor, SonarQube) with Azure AD authentication.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/azure-ad-sso" ~/.claude/skills/majiayu000-claude-skill-registry-azure-ad-sso && rm -rf "$T"
manifest:
skills/data/azure-ad-sso/SKILL.mdsource content
Azure AD SSO Integration Skill
Overview
This skill provides comprehensive guidance for implementing Azure AD (Entra ID) OAuth2/OIDC Single Sign-On for applications deployed on Kubernetes clusters, including access restriction by Azure AD groups.
Quick Reference
Supported Applications
| Application | Provider | Redirect URI Pattern | Group Sync |
|---|---|---|---|
| DefectDojo | | | Yes |
| Grafana | | | Yes |
| ArgoCD | (Dex) | | Yes |
| Harbor | | | Yes |
| SonarQube | or | | Yes |
| OAuth2 Proxy | | | Yes |
| Keycloak | | | Yes |
Authentication Flow Decision
┌─────────────────────────────────────────────────────────────────┐ │ Access Control Decision │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ Q: Who should access this application? │ │ │ │ ├─ Everyone in tenant ──► appRoleAssignmentRequired=false │ │ │ │ │ └─ Specific groups ────► appRoleAssignmentRequired=true │ │ + Assign groups to Enterprise App │ │ │ └─────────────────────────────────────────────────────────────────┘
Implementation Workflow
Phase 1: Azure AD App Registration
# 1. Create App Registration APP_NAME="<application>-<environment>" REDIRECT_URI="https://<app-domain>/complete/<provider>/" APP_ID=$(az ad app create \ --display-name "$APP_NAME" \ --sign-in-audience "AzureADMyOrg" \ --web-redirect-uris "$REDIRECT_URI" \ --query appId -o tsv) echo "Application (client) ID: $APP_ID" # 2. Get Tenant ID TENANT_ID=$(az account show --query tenantId -o tsv) echo "Directory (tenant) ID: $TENANT_ID" # 3. Create Client Secret SECRET=$(az ad app credential reset \ --id $APP_ID \ --append \ --years 1 \ --query password -o tsv) echo "Client Secret: $SECRET" # Save immediately!
Phase 2: Enable Group Claims
# Enable security group claims in tokens az ad app update --id $APP_ID --set groupMembershipClaims=SecurityGroup # Add Group.Read.All permission (delegated) az ad app permission add \ --id $APP_ID \ --api 00000003-0000-0000-c000-000000000000 \ --api-permissions 5f8c59db-677d-491f-a6b8-5f174b11ec1d=Scope # Grant admin consent az ad app permission admin-consent --id $APP_ID
Phase 3: Restrict Access by Group (CRITICAL)
# Get Service Principal object ID SP_ID=$(az ad sp list --filter "appId eq '$APP_ID'" --query "[0].id" -o tsv) # Enable user assignment requirement az ad sp update --id $SP_ID --set appRoleAssignmentRequired=true # Get the group ID to restrict access GROUP_ID=$(az ad group show --group "G-Usuarios-<App>-Admin" --query id -o tsv) # Assign group to the application (only these users can login) az rest --method POST \ --uri "https://graph.microsoft.com/v1.0/servicePrincipals/$SP_ID/appRoleAssignments" \ --headers "Content-Type=application/json" \ --body "{ \"principalId\": \"$GROUP_ID\", \"principalType\": \"Group\", \"appRoleId\": \"00000000-0000-0000-0000-000000000000\", \"resourceId\": \"$SP_ID\" }"
Phase 4: Store Secret in Key Vault
az keyvault secret set \ --vault-name "<keyvault-name>" \ --name "<app>-azuread-client-secret" \ --value "$SECRET"
Secret Management
SecretProviderClass Template
apiVersion: secrets-store.csi.x-k8s.io/v1 kind: SecretProviderClass metadata: name: <app>-secrets namespace: <namespace> spec: provider: azure parameters: usePodIdentity: "false" useVMManagedIdentity: "true" userAssignedIdentityID: "<managed-identity-client-id>" keyvaultName: "<keyvault-name>" tenantId: "<azure-tenant-id>" objects: | array: - | objectName: <app>-azuread-client-secret objectType: secret objectAlias: AZURE_AD_CLIENT_SECRET secretObjects: - secretName: <app>-azure-ad type: Opaque data: - objectName: AZURE_AD_CLIENT_SECRET key: client-secret
Pod Volume Mount
volumes: - name: secrets-store csi: driver: secrets-store.csi.k8s.io readOnly: true volumeAttributes: secretProviderClass: "<app>-secrets" volumeMounts: - name: secrets-store mountPath: "/mnt/secrets-store" readOnly: true
Application Configurations
DefectDojo
# Enable SSO extraEnv: - name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_ENABLED value: "True" - name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_KEY value: "<client-id>" - name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_TENANT_ID value: "<tenant-id>" - name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_SECRET valueFrom: secretKeyRef: name: defectdojo key: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_SECRET # Group sync - name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_GET_GROUPS value: "True" - name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_CLEANUP_GROUPS value: "True" - name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_GROUPS_FILTER value: "^G-Usuarios-DefectDojo-.*" # CRITICAL: For apps behind reverse proxy - name: DD_SECURE_PROXY_SSL_HEADER value: "True"
Grafana
grafana.ini: auth.azuread: enabled: true name: Azure AD allow_sign_up: true client_id: "<client-id>" client_secret: "${GF_AUTH_AZUREAD_CLIENT_SECRET}" scopes: openid email profile auth_url: https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize token_url: https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token allowed_groups: "<admin-group-id> <viewer-group-id>" role_attribute_path: contains(groups[*], '<admin-group-id>') && 'Admin' || 'Viewer'
ArgoCD (via Dex)
configs: cm: dex.config: | connectors: - type: microsoft id: microsoft name: Azure AD config: clientID: "<client-id>" clientSecret: $dex.azure.clientSecret tenant: "<tenant-id>" redirectURI: https://<argocd-domain>/api/dex/callback groups: - <admin-group-id> rbac: policy.csv: | g, <admin-group-id>, role:admin
Harbor
externalURL: https://harbor.<domain> core: oidc: name: "azure" endpoint: "https://login.microsoftonline.com/<tenant-id>/v2.0" clientId: "<client-id>" clientSecret: "<from-secret>" scope: "openid,profile,email" groupsClaim: "groups" adminGroup: "<admin-group-id>" autoOnboard: true
Troubleshooting
Error Reference
| Error Code | Description | Solution |
|---|---|---|
| AADSTS50011 | Reply URL mismatch | Verify exact redirect URI including trailing slash |
| AADSTS50105 | User not assigned | Add user/group to Enterprise App assignments |
| AADSTS700016 | App not found | Check client ID and tenant ID |
| AADSTS7000218 | Secret expired | Rotate secret in Key Vault, restart pods |
| AADSTS90102 | Invalid redirect_uri | Check for reverse proxy |
| AADSTS65001 | Consent not granted | Run |
Common Issues
Malformed redirect_uri (Django apps behind proxy)
Symptom:
redirect_uri=https,%20https://...
Root cause:
DD_SECURE_PROXY_SSL_HEADER set incorrectly
Fix:
- name: DD_SECURE_PROXY_SSL_HEADER value: "True" # NOT "HTTP_X_FORWARDED_PROTO,https"
Groups not syncing
# Verify group claims enabled az ad app show --id <app-id> --query groupMembershipClaims # Check API permissions az ad app permission list --id <app-id> # Verify group exists and user is member az ad group member check --group "<group-name>" --member-id "<user-object-id>"
Secret not syncing from Key Vault
# Check SecretProviderClass kubectl describe secretproviderclass <name> -n <namespace> # Check CSI driver pods kubectl get pods -n kube-system | grep secrets-store # Check managed identity access az keyvault show --name <vault> --query properties.accessPolicies
Diagnostic Commands
# Test OAuth redirect curl -sS -k -D - -o /dev/null "https://<app>/login/<provider>/" 2>&1 | grep -i location # Check environment variables in pod kubectl exec -n <ns> deploy/<app> -c <container> -- env | grep -i azure # Decode JWT token (after login, from browser dev tools) # Use https://jwt.io to decode and verify claims
Security Best Practices
- Never hardcode secrets - Always use Key Vault + CSI Driver
- Use managed identities - Avoid service principal credentials
- Restrict access by group - Enable
appRoleAssignmentRequired=true - Rotate secrets - Set calendar reminders before expiration
- Use HTTPS only - All redirect URIs must use HTTPS
- Single tenant - Never use multi-tenant for internal apps
- Audit logging - Enable Azure AD sign-in logs
Environment Reference
| Environment | Key Vault | Managed Identity | Tenant ID |
|---|---|---|---|
| cafehyna-dev | | | |
| cafehyna-hub | | | |
| cafehyna-prd | | | |
Detailed Reference
For complete implementation examples:
- references/azure-ad-sso-guide.md - Full guide with manifests
- references/app-configs.md - Application-specific configurations
- references/troubleshooting.md - Extended troubleshooting guide