entra-app-registration
Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration. USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication. DO NOT USE FOR: Azure RBAC or role assignments (use azure-rbac), Key Vault secrets (use azure-keyvault-expiration-audit), Azure resource security (use azure-security).
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install microsoft-skills-entra-app-registration
Repository
Skill path: .github/plugins/azure-skills/skills/entra-app-registration
Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration. USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication. DO NOT USE FOR: Azure RBAC or role assignments (use azure-rbac), Key Vault secrets (use azure-keyvault-expiration-audit), Azure resource security (use azure-security).
Open repositoryBest for
Primary workflow: Run DevOps.
Technical facets: Full Stack, Backend, Security, Integration.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: microsoft.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install entra-app-registration into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/microsoft/skills before adding entra-app-registration to shared team environments
- Use entra-app-registration for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: entra-app-registration
description: |
Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration.
USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication.
DO NOT USE FOR: Azure RBAC or role assignments (use azure-rbac), Key Vault secrets (use azure-keyvault-expiration-audit), Azure resource security (use azure-security).
---
## Overview
Microsoft Entra ID (formerly Azure Active Directory) is Microsoft's cloud-based identity and access management service. App registrations allow applications to authenticate users and access Azure resources securely.
### Key Concepts
| Concept | Description |
|---------|-------------|
| **App Registration** | Configuration that allows an app to use Microsoft identity platform |
| **Application (Client) ID** | Unique identifier for your application |
| **Tenant ID** | Unique identifier for your Azure AD tenant/directory |
| **Client Secret** | Password for the application (confidential clients only) |
| **Redirect URI** | URL where authentication responses are sent |
| **API Permissions** | Access scopes your app requests |
| **Service Principal** | Identity created in your tenant when you register an app |
### Application Types
| Type | Use Case |
|------|----------|
| **Web Application** | Server-side apps, APIs |
| **Single Page App (SPA)** | JavaScript/React/Angular apps |
| **Mobile/Native App** | Desktop, mobile apps |
| **Daemon/Service** | Background services, APIs |
## Core Workflow
### Step 1: Register the Application
Create an app registration in the Azure portal or using Azure CLI.
**Portal Method:**
1. Navigate to Azure Portal → Microsoft Entra ID → App registrations
2. Click "New registration"
3. Provide name, supported account types, and redirect URI
4. Click "Register"
**CLI Method:** See [references/cli-commands.md](references/cli-commands.md)
**IaC Method:** See [references/BICEP-EXAMPLE.bicep](references/BICEP-EXAMPLE.bicep)
It's highly recommended to use the IaC to manage Entra app registration if you already use IaC in your project, need a scalable solution for managing lots of app registrations or need fine-grained audit history of the configuration changes.
### Step 2: Configure Authentication
Set up authentication settings based on your application type.
- **Web Apps**: Add redirect URIs, enable ID tokens if needed
- **SPAs**: Add redirect URIs, enable implicit grant flow if necessary
- **Mobile/Desktop**: Use `http://localhost` or custom URI scheme
- **Services**: No redirect URI needed for client credentials flow
### Step 3: Configure API Permissions
Grant your application permission to access Microsoft APIs or your own APIs.
**Common Microsoft Graph Permissions:**
- `User.Read` - Read user profile
- `User.ReadWrite.All` - Read and write all users
- `Directory.Read.All` - Read directory data
- `Mail.Send` - Send mail as a user
**Details:** See [references/api-permissions.md](references/api-permissions.md)
### Step 4: Create Client Credentials (if needed)
For confidential client applications (web apps, services), create a client secret, certificate or federated identity credential.
**Client Secret:**
- Navigate to "Certificates & secrets"
- Create new client secret
- Copy the value immediately (only shown once)
- Store securely (Key Vault recommended)
**Certificate:** For production environments, use certificates instead of secrets for enhanced security. Upload certificate via "Certificates & secrets" section.
**Federated Identity Credential:** For dynamically authenticating the confidential client to Entra platform.
### Step 5: Implement OAuth Flow
Integrate the OAuth flow into your application code.
**See:**
- [references/oauth-flows.md](references/oauth-flows.md) - OAuth 2.0 flow details
- [references/console-app-example.md](references/console-app-example.md) - Console app implementation
## Common Patterns
### Pattern 1: First-Time App Registration
Walk user through their first app registration step-by-step.
**Required Information:**
- Application name
- Application type (web, SPA, mobile, service)
- Redirect URIs (if applicable)
- Required permissions
**Script:** See [references/first-app-registration.md](references/first-app-registration.md)
### Pattern 2: Console Application with User Authentication
Create a .NET/Python/Node.js console app that authenticates users.
**Required Information:**
- Programming language (C#, Python, JavaScript, etc.)
- Authentication library (MSAL recommended)
- Required permissions
**Example:** See [references/console-app-example.md](references/console-app-example.md)
### Pattern 3: Service-to-Service Authentication
Set up daemon/service authentication without user interaction.
**Required Information:**
- Service/app name
- Target API/resource
- Whether to use secret or certificate
**Implementation:** Use Client Credentials flow (see [references/oauth-flows.md#client-credentials-flow](references/oauth-flows.md#client-credentials-flow))
## MCP Tools and CLI
### Azure CLI Commands
| Command | Purpose |
|---------|---------|
| `az ad app create` | Create new app registration |
| `az ad app list` | List app registrations |
| `az ad app show` | Show app details |
| `az ad app permission add` | Add API permission |
| `az ad app credential reset` | Generate new client secret |
| `az ad sp create` | Create service principal |
**Complete reference:** See [references/cli-commands.md](references/cli-commands.md)
### Microsoft Authentication Library (MSAL)
MSAL is the recommended library for integrating Microsoft identity platform.
**Supported Languages:**
- .NET/C# - `Microsoft.Identity.Client`
- JavaScript/TypeScript - `@azure/msal-browser`, `@azure/msal-node`
- Python - `msal`
**Examples:** See [references/console-app-example.md](references/console-app-example.md)
## Security Best Practices
| Practice | Recommendation |
|----------|---------------|
| **Never hardcode secrets** | Use environment variables, Azure Key Vault, or managed identity |
| **Rotate secrets regularly** | Set expiration, automate rotation |
| **Use certificates over secrets** | More secure for production |
| **Least privilege permissions** | Request only required API permissions |
| **Enable MFA** | Require multi-factor authentication for users |
| **Use managed identity** | For Azure-hosted apps, avoid secrets entirely |
| **Validate tokens** | Always validate issuer, audience, expiration |
| **Use HTTPS only** | All redirect URIs must use HTTPS (except localhost) |
| **Monitor sign-ins** | Use Entra ID sign-in logs for anomaly detection |
## SDK Quick References
- **Azure Identity**: [Python](references/sdk/azure-identity-py.md) | [.NET](references/sdk/azure-identity-dotnet.md) | [TypeScript](references/sdk/azure-identity-ts.md) | [Java](references/sdk/azure-identity-java.md) | [Rust](references/sdk/azure-identity-rust.md)
- **Key Vault (secrets)**: [Python](references/sdk/azure-keyvault-py.md) | [TypeScript](references/sdk/azure-keyvault-secrets-ts.md)
- **Auth Events**: [.NET](references/sdk/microsoft-azure-webjobs-extensions-authentication-events-dotnet.md)
## References
- [OAuth Flows](references/oauth-flows.md) - Detailed OAuth 2.0 flow explanations
- [CLI Commands](references/cli-commands.md) - Azure CLI reference for app registrations
- [Console App Example](references/console-app-example.md) - Complete working examples
- [First App Registration](references/first-app-registration.md) - Step-by-step guide for beginners
- [API Permissions](references/api-permissions.md) - Understanding and configuring permissions
- [Troubleshooting](references/troubleshooting.md) - Common issues and solutions
## External Resources
- [Microsoft Identity Platform Documentation](https://learn.microsoft.com/entra/identity-platform/)
- [OAuth 2.0 and OpenID Connect protocols](https://learn.microsoft.com/entra/identity-platform/v2-protocols)
- [MSAL Documentation](https://learn.microsoft.com/entra/msal/)
- [Microsoft Graph API](https://learn.microsoft.com/graph/)
---
## Referenced Files
> The following files are referenced in this skill and included for context.
### references/cli-commands.md
```markdown
# Azure CLI Commands for App Registration
This document provides a comprehensive reference for managing Microsoft Entra app registrations using Azure CLI.
## Prerequisites
```bash
# Ensure Azure CLI is installed
az version
# Login to Azure
az login
# Set default subscription (optional)
az account set --subscription "Your Subscription Name"
```
## App Registration Management
### Create App Registration
**Basic app registration:**
```bash
az ad app create --display-name "MyApplication"
```
**Web application with redirect URI:**
```bash
az ad app create \
--display-name "MyWebApp" \
--web-redirect-uris "https://myapp.com/callback" \
--sign-in-audience "AzureADMyOrg"
```
**Single Page Application (SPA):**
```bash
az ad app create \
--display-name "MySpaApp" \
--spa-redirect-uris "http://localhost:3000" \
--sign-in-audience "AzureADMyOrg"
```
**Public client (Desktop/Mobile app):**
```bash
az ad app create \
--display-name "MyDesktopApp" \
--public-client-redirect-uris "http://localhost" \
--sign-in-audience "AzureADMyOrg"
```
**Multi-tenant application:**
```bash
az ad app create \
--display-name "MyMultiTenantApp" \
--web-redirect-uris "https://myapp.com/callback" \
--sign-in-audience "AzureADMultipleOrgs"
```
### Sign-in Audience Options
| Value | Description |
|-------|-------------|
| `AzureADMyOrg` | Single tenant (default) |
| `AzureADMultipleOrgs` | Multi-tenant (any Azure AD) |
| `AzureADandPersonalMicrosoftAccount` | Multi-tenant + personal Microsoft accounts |
| `PersonalMicrosoftAccount` | Personal Microsoft accounts only |
## List and Query Apps
### List all app registrations
```bash
az ad app list --output table
```
### List apps with custom query
```bash
# Filter by display name
az ad app list --display-name "MyApp" --output table
# Get specific fields
az ad app list --query "[].{Name:displayName, AppId:appId}" --output table
```
### Get app details
```bash
# By display name
az ad app show --id $(az ad app list --display-name "MyApp" --query "[0].appId" -o tsv)
# By application ID
az ad app show --id "YOUR_APPLICATION_ID"
```
### Get Application (Client) ID
```bash
APP_ID=$(az ad app list --display-name "MyApp" --query "[0].appId" -o tsv)
echo "Application ID: $APP_ID"
```
### Get Object ID
```bash
OBJECT_ID=$(az ad app list --display-name "MyApp" --query "[0].id" -o tsv)
echo "Object ID: $OBJECT_ID"
```
## Update App Registration
### Add redirect URIs
**Web app:**
```bash
az ad app update --id $APP_ID \
--web-redirect-uris "https://myapp.com/callback" "https://myapp.com/auth"
```
**SPA:**
```bash
az ad app update --id $APP_ID \
--spa-redirect-uris "http://localhost:3000" "http://localhost:5000"
```
**Public client:**
```bash
az ad app update --id $APP_ID \
--public-client-redirect-uris "http://localhost" "myapp://auth"
```
## Client Credentials (Secrets & Certificates)
### Create client secret
```bash
# Create secret with default expiration
az ad app credential reset --id $APP_ID
# Create secret with custom expiration
az ad app credential reset --id $APP_ID --years 1
# Create secret with specific end date
az ad app credential reset --id $APP_ID --end-date "2025-12-31"
```
**Save the output:**
```json
{
"appId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"password": "your-secret-value-SAVE-THIS",
"tenant": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
```
**⚠️ Important:** Resetting Client credential will delete all existing credentials.
**⚠️ Important:** The secret value is only shown once. Store it securely (e.g., Azure Key Vault).
### List client credentials
```bash
# List all credentials (secrets and certificates)
az ad app credential list --id $APP_ID
```
### Delete client secret
```bash
# Get key ID from credential list
az ad app credential list --id $APP_ID --query "[].{KeyId:keyId, Type:type}" -o table
# Delete specific credential
az ad app credential delete --id $APP_ID --key-id "KEY_ID_HERE"
```
### Upload certificate
```bash
# Upload certificate from file
az ad app credential reset --id $APP_ID --cert "@path/to/cert.pem"
```
## API Permissions
### Add API permissions
**Microsoft Graph User.Read:**
```bash
GRAPH_RESOURCE_ID="00000003-0000-0000-c000-000000000000" # Microsoft Graph
USER_READ_ID="e1fe6dd8-ba31-4d61-89e7-88639da4683d" # User.Read permission
az ad app permission add --id $APP_ID \
--api $GRAPH_RESOURCE_ID \
--api-permissions "$USER_READ_ID=Scope"
```
**Microsoft Graph Mail.Read (delegated):**
```bash
MAIL_READ_ID="570282fd-fa5c-430d-a7fd-fc8dc98a9dca" # Mail.Read permission
az ad app permission add --id $APP_ID \
--api $GRAPH_RESOURCE_ID \
--api-permissions "$MAIL_READ_ID=Scope"
```
**Microsoft Graph User.Read.All (application):**
```bash
USER_READ_ALL_ID="df021288-bdef-4463-88db-98f22de89214" # User.Read.All application permission
az ad app permission add --id $APP_ID \
--api $GRAPH_RESOURCE_ID \
--api-permissions "$USER_READ_ALL_ID=Role"
```
**Note:** Use `Scope` for delegated permissions, `Role` for application permissions.
### Common Permission IDs
**Microsoft Graph (00000003-0000-0000-c000-000000000000):**
| Permission | ID | Type |
|------------|-----|------|
| User.Read | e1fe6dd8-ba31-4d61-89e7-88639da4683d | Delegated |
| User.ReadWrite | b4e74841-8e56-480b-be8b-910348b18b4c | Delegated |
| Mail.Read | 570282fd-fa5c-430d-a7fd-fc8dc98a9dca | Delegated |
| Mail.Send | e383f46e-2787-4529-855e-0e479a3ffac0 | Delegated |
| Calendars.Read | 465a38f9-76ea-45b9-9f34-9e8b0d4b0b42 | Delegated |
| User.Read.All | df021288-bdef-4463-88db-98f22de89214 | Application |
| Directory.Read.All | 7ab1d382-f21e-4acd-a863-ba3e13f7da61 | Application |
### Grant admin consent
```bash
# Grant admin consent for all permissions
az ad app permission admin-consent --id $APP_ID
```
**Note:** Admin consent is required for application permissions and some delegated permissions.
### List permissions
```bash
az ad app permission list --id $APP_ID
```
### Delete permission
```bash
# Remove specific permission
az ad app permission delete --id $APP_ID \
--api $GRAPH_RESOURCE_ID \
--permission-id $USER_READ_ID
```
## Service Principal Management
### Create service principal
```bash
# Create service principal for the app
az ad sp create --id $APP_ID
```
### List service principals
```bash
az ad sp list --display-name "MyApp"
```
### Get service principal details
```bash
az ad sp show --id $APP_ID
```
### Delete service principal
```bash
az ad sp delete --id $APP_ID
```
## App Roles and Claims
### Get app roles
```bash
az ad app show --id $APP_ID --query "appRoles"
```
### Get optional claims
```bash
az ad app show --id $APP_ID --query "optionalClaims"
```
## Owners
### List app owners
```bash
az ad app owner list --id $APP_ID
```
### Add owner
```bash
# Add user as owner
USER_OBJECT_ID=$(az ad user show --id "[email protected]" --query "id" -o tsv)
az ad app owner add --id $APP_ID --owner-object-id $USER_OBJECT_ID
```
### Remove owner
```bash
az ad app owner remove --id $APP_ID --owner-object-id $USER_OBJECT_ID
```
## Delete App Registration
```bash
# Delete app registration (and associated service principal)
az ad app delete --id $APP_ID
```
## Tenant and Identity Information
### Get tenant ID
```bash
az account show --query tenantId -o tsv
```
### Get current user information
```bash
az ad signed-in-user show
```
### Get user by email
```bash
az ad user show --id "[email protected]"
```
### Get user object ID
```bash
az ad user show --id "[email protected]" --query "id" -o tsv
```
### List all users
```bash
az ad user list --output table
```
## Scripting Examples
### Complete app setup script
```bash
#!/bin/bash
# Variables
APP_NAME="MyApplication"
REDIRECT_URI="http://localhost:3000"
echo "Creating app registration..."
APP_ID=$(az ad app create \
--display-name "$APP_NAME" \
--spa-redirect-uris "$REDIRECT_URI" \
--query "appId" -o tsv)
echo "App created with ID: $APP_ID"
echo "Adding Microsoft Graph permissions..."
GRAPH_RESOURCE_ID="00000003-0000-0000-c000-000000000000"
USER_READ_ID="e1fe6dd8-ba31-4d61-89e7-88639da4683d"
az ad app permission add --id $APP_ID \
--api $GRAPH_RESOURCE_ID \
--api-permissions "$USER_READ_ID=Scope"
echo "Granting admin consent..."
az ad app permission admin-consent --id $APP_ID
echo "Creating service principal..."
az ad sp create --id $APP_ID
TENANT_ID=$(az account show --query tenantId -o tsv)
echo ""
echo "App registration complete!"
echo "Application (Client) ID: $APP_ID"
echo "Tenant ID: $TENANT_ID"
echo "Redirect URI: $REDIRECT_URI"
```
### Cleanup script
```bash
#!/bin/bash
# Delete all apps matching pattern
az ad app list --display-name "Test*" --query "[].appId" -o tsv | while read APP_ID; do
echo "Deleting app: $APP_ID"
az ad app delete --id $APP_ID
done
```
```
### references/api-permissions.md
```markdown
# API Permissions Guide
This document explains how to configure and manage API permissions for your Microsoft Entra app registration.
## Permission Types
### Delegated Permissions (User Context)
**What:** Application acts on behalf of a signed-in user
**When to use:**
- User is present and can consent
- App needs to access resources as the user
- Interactive authentication flows
**Examples:**
- Read user's email
- Update user's calendar
- Access user's OneDrive files
**Scope format:** User must consent (or admin pre-consents)
### Application Permissions (App Context)
**What:** Application acts with its own identity (no user)
**When to use:**
- Background services, daemons
- Scheduled jobs
- API-to-API calls without user
**Examples:**
- Read all users in organization
- Send mail as any user
- Access all SharePoint sites
**Requirement:** Always requires admin consent
## Permission Scopes
### Understanding Scopes
**Scope:** A string that defines what access is granted
**Format:**
```
{resource}/{permission_name}
Examples:
https://graph.microsoft.com/User.Read
https://graph.microsoft.com/Mail.Send
api://myapi-id/access_as_user
```
### .default Scope
Special scope that includes all configured permissions:
```
https://graph.microsoft.com/.default
api://your-api-id/.default
```
**When to use:**
- Client credentials flow (always)
- Want all pre-configured permissions
- Migrating from v1.0 endpoint
## Microsoft Graph Permissions
### Common Delegated Permissions
| Permission | What it allows | Admin Consent Required |
|------------|---------------|----------------------|
| `User.Read` | Read signed-in user's profile | No |
| `User.ReadWrite` | Read and update user profile | No |
| `User.ReadBasic.All` | Read basic info of all users | No |
| `User.Read.All` | Read all users' full profiles | Yes |
| `Mail.Read` | Read user's mail | No |
| `Mail.ReadWrite` | Read and write user's mail | No |
| `Mail.Send` | Send mail as user | No |
| `Calendars.Read` | Read user's calendars | No |
| `Calendars.ReadWrite` | Read and write calendars | No |
| `Files.Read.All` | Read all files user can access | No |
| `Sites.Read.All` | Read items in all site collections | Yes |
| `Directory.Read.All` | Read directory data | Yes |
| `Directory.ReadWrite.All` | Read and write directory data | Yes |
### Common Application Permissions
| Permission | What it allows | Admin Consent Required |
|------------|---------------|----------------------|
| `User.Read.All` | Read all users' full profiles | Yes (Always) |
| `User.ReadWrite.All` | Read and write all users' profiles | Yes (Always) |
| `Mail.Read` | Read mail in all mailboxes | Yes (Always) |
| `Mail.Send` | Send mail as any user | Yes (Always) |
| `Calendars.Read` | Read calendars in all mailboxes | Yes (Always) |
| `Directory.Read.All` | Read directory data | Yes (Always) |
| `Directory.ReadWrite.All` | Read and write directory data | Yes (Always) |
| `Group.ReadWrite.All` | Read and write all groups | Yes (Always) |
## Adding Permissions
### Azure Portal Method
1. Navigate to your app registration
2. Click **"API permissions"** in left menu
3. Click **"+ Add a permission"**
4. Choose API source:
- **Microsoft APIs** (Graph, Office 365, etc.)
- **APIs my organization uses** (custom APIs)
- **My APIs** (your own APIs)
5. Select permission type:
- **Delegated permissions** (user context)
- **Application permissions** (app context)
6. Search and select permissions
7. Click **"Add permissions"**
See [cli-commands.md](cli-commands.md) for az cli commands to add API permissions programmatically.
## Finding Permission IDs
### Method 1: Azure Portal
1. Go to Microsoft Entra ID → Enterprise applications
2. Search for "Microsoft Graph"
3. Click on it → Permissions
4. Browse available permissions and copy IDs
### Method 2: Microsoft Graph Explorer
1. Visit https://developer.microsoft.com/graph/graph-explorer
2. Click "Modify permissions"
3. Browse and view permission details
### Method 3: Microsoft Documentation
Visit: https://learn.microsoft.com/en-us/graph/permissions-reference
### Method 4: Azure CLI Query
```bash
# List all Graph permissions (warning: long output)
az ad sp list --filter "appId eq '00000003-0000-0000-c000-000000000000'" \
--query "[0].{delegated:oauth2PermissionScopes,application:appRoles}" -o json
```
## Granting Admin Consent
### When Admin Consent is Required
**Always required for:**
- All application permissions
- High-privilege delegated permissions
- When organization disables user consent
**Examples requiring admin consent:**
- `User.Read.All` (read all users)
- `Directory.Read.All` (read directory)
- `Mail.Read` (application permission)
- `Sites.Read.All` (read all SharePoint sites)
### How to Grant Admin Consent
**Portal Method:**
1. Go to API permissions
2. Click **"Grant admin consent for [Your Org]"**
3. Confirm the action
4. Check for green checkmarks next to permissions
**CLI Method:**
```bash
az ad app permission admin-consent --id $APP_ID
```
### Verifying Consent Status
**Portal:** Look for green checkmarks in "Status" column
**CLI:**
```bash
az ad app permission list --id $APP_ID
```
Look for `consentType: "AllPrincipals"` (admin consented)
## Custom API Permissions
### Exposing Your API
If you're building an API that other apps will call:
1. In your API's app registration, go to **"Expose an API"**
2. Set **Application ID URI**: `api://your-api-id`
3. Click **"+ Add a scope"**
4. Configure scope:
- **Scope name:** `access_as_user`
- **Who can consent:** Admins and users
- **Display name:** "Access MyAPI as user"
- **Description:** Clear description of what this allows
5. Click **"Add scope"**
## Effective Permissions
### User + App Permissions
**Delegated permissions:** Intersection of user's permissions and app's permissions
Example:
- User can: Read all users
- App granted: User.Read.All
- **Effective:** Read all users ✅
- User can: Only read their own profile
- App granted: User.Read.All
- **Effective:** Only read own profile (limited by user's rights)
**Application permissions:** Only app's permissions matter (no user context)
## Troubleshooting Permissions
### "Insufficient privileges" Error
**Causes:**
- Permission not added to app registration
- Admin consent not granted
- User lacks permission in directory
- Accessing resource outside permission scope
**Solutions:**
1. Check API permissions in portal
2. Grant admin consent if needed
3. Verify user has access to resource
4. Use correct permission scope
### "Consent required" Error
**Causes:**
- User hasn't consented to permissions
- Admin consent required but not granted
- Token obtained before permission added
**Solutions:**
1. Request user consent (interactive flow)
2. Admin grants consent (portal or CLI)
3. Acquire new token after adding permissions
### Permission Appears Granted but Doesn't Work
**Possible issues:**
- Using old cached token (get new one)
- Permission is delegated but user lacks rights
- API requires additional configuration
- Permission deprecated (use new one)
**Debug steps:**
1. Decode access token: https://jwt.ms
2. Check `scp` claim (delegated) or `roles` claim (application)
3. Verify permission is present in token
4. Check if permission is correct type (delegated vs application)
## Permission Best Practices
### Development
✅ **Do:**
- Start with minimal permissions
- Add incrementally as features require
- Test with non-admin accounts
- Document why each permission is needed
❌ **Don't:**
- Request all permissions "just in case"
- Use admin account for testing only
- Forget to grant admin consent for app permissions
### Production
✅ **Do:**
- Review permissions quarterly
- Remove unused permissions
- Use least privilege principle
- Monitor permission usage
- Document all permissions in README
❌ **Don't:**
- Grant excessive permissions for convenience
- Use application permissions when delegated would work
- Forget to rotate admin consent approvals
### Security
✅ **Do:**
- Prefer delegated over application permissions
- Implement proper scope validation
- Log permission usage
- Handle consent errors gracefully
❌ **Don't:**
- Hardcode permission scopes in multiple places
- Skip token validation
- Ignore scope mismatches
- Cache permissions indefinitely
## Reference Tables
### Microsoft Graph Permission IDs
**Delegated Permissions:**
```
User.Read : e1fe6dd8-ba31-4d61-89e7-88639da4683d
User.ReadWrite : b4e74841-8e56-480b-be8b-910348b18b4c
User.ReadBasic.All : b340eb25-3456-403f-be2f-af7a0d370277
Mail.Read : 570282fd-fa5c-430d-a7fd-fc8dc98a9dca
Mail.ReadWrite : 024d486e-b451-40bb-833d-3e66d98c5c73
Mail.Send : e383f46e-2787-4529-855e-0e479a3ffac0
Calendars.Read : 465a38f9-76ea-45b9-9f34-9e8b0d4b0b42
Calendars.ReadWrite : 1ec239c2-d7c9-4623-a91a-a9775856bb36
Files.Read.All : df85f4d6-205c-4ac5-a5ea-6bf408dba283
```
**Application Permissions:**
```
User.Read.All : df021288-bdef-4463-88db-98f22de89214
User.ReadWrite.All : 741f803b-c850-494e-b5df-cde7c675a1ca
Mail.Read : 810c84a8-4a9e-49e6-bf7d-12d183f40d01
Mail.Send : b633e1c5-b582-4048-a93e-9f11b44c7e96
Directory.Read.All : 7ab1d382-f21e-4acd-a863-ba3e13f7da61
Directory.ReadWrite.All : 19dbc75e-c2e2-444c-a770-ec69d8559fc7
```
**Note:** Permission IDs may change. Always verify against the official [Microsoft Graph Permissions Reference](https://learn.microsoft.com/en-us/graph/permissions-reference) for the most current values.
## Additional Resources
- [Microsoft Graph Permissions Reference](https://learn.microsoft.com/en-us/graph/permissions-reference)
- [Permission Types](https://learn.microsoft.com/en-us/entra/identity-platform/permissions-consent-overview)
- [Admin Consent Workflow](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/configure-admin-consent-workflow)
- [Consent Framework](https://learn.microsoft.com/en-us/entra/identity-platform/consent-framework)
```
### references/oauth-flows.md
```markdown
# OAuth 2.0 Flows
This document provides an illustration of OAuth 2.0 authentication flows supported by Microsoft Entra ID.
**Note:** All the following implementation steps are for illustration purposes. It's always recommended to use a library to handle the authentication flow.
## Authorization Code Flow
### Flow Steps
```
1. User → App: Navigate to app's web UI
2. App → User: Redirect to Microsoft login
3. User → Entra ID: Authenticate & consent
4. Entra ID → App: Authorization code (via redirect URI)
5. App → Entra ID: Exchange code for tokens (with client secret)
6. Entra ID → App: Access token + refresh token + ID token
7. App → API: Call API with access token
```
### Implementation Steps
#### 1. Build Authorization URL
```
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id={application_id}
&response_type=code
&redirect_uri={redirect_uri}
&response_mode=query
&scope={scopes}
&state={random_state}
```
**Parameters:**
- `tenant`: Your tenant ID or `common` for multi-tenant
- `client_id`: Application (client) ID from app registration
- `redirect_uri`: Must match exactly what's registered
- `scope`: Space-separated permissions (e.g., `openid profile User.Read`)
- `state`: Random value to prevent CSRF attacks
#### 2. User Authenticates
User is redirected to Microsoft login page, authenticates, and grants consent.
#### 3. Receive Authorization Code
App receives callback at redirect URI:
```
https://your-app.com/callback?
code={authorization_code}
&state={state_value}
```
**Validation:**
- Verify `state` matches what you sent
- Extract `code` parameter
#### 4. Exchange Code for Tokens
```http
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id={application_id}
&scope={scopes}
&code={authorization_code}
&redirect_uri={redirect_uri}
&grant_type=authorization_code
&client_secret={client_secret}
```
**Response:**
```json
{
"access_token": "eyJ0eXAi...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "M.R3_BAY...",
"id_token": "eyJ0eXAi..."
}
```
#### 5. Use Access Token
```http
GET https://graph.microsoft.com/v1.0/me
Authorization: Bearer {access_token}
```
## Authorization Code Flow with PKCE
PKCE (Proof Key for Code Exchange) adds security for public clients that cannot securely store a client secret.
### Flow Steps
```
1. App: Generate code verifier (random string)
2. App: Generate code challenge (SHA256 hash of verifier)
3. App → Entra ID: Authorization request with code challenge
4. User → Entra ID: Authenticate & consent
5. Entra ID → App: Authorization code
6. App → Entra ID: Exchange code + code verifier for token
7. Entra ID: Validates verifier matches challenge
8. Entra ID → App: Access token + ID token
```
### Implementation Steps
#### 1. Generate PKCE Values
**Code Verifier:** 43-128 character random string
```javascript
// JavaScript example
const codeVerifier = generateRandomString(128);
```
**Code Challenge:** Base64URL-encoded SHA256 hash of verifier
```javascript
const codeChallenge = base64URLEncode(sha256(codeVerifier));
```
#### 2. Build Authorization URL
```
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id={application_id}
&response_type=code
&redirect_uri={redirect_uri}
&scope={scopes}
&state={state}
&code_challenge={code_challenge}
&code_challenge_method=S256
```
#### 3. Exchange Code for Tokens (No Secret)
```http
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id={application_id}
&scope={scopes}
&code={authorization_code}
&redirect_uri={redirect_uri}
&grant_type=authorization_code
&code_verifier={code_verifier}
```
## Client Credentials Flow
### Flow Steps
```
1. App → Entra ID: Request token with client ID + secret
2. Entra ID: Validate credentials
3. Entra ID → App: Access token (application permissions)
4. App → API: Call API with token
```
### Implementation Steps
#### 1. Configure Application Permissions
In app registration:
1. Go to "API permissions"
2. Add **Application** permissions (not delegated)
3. Grant admin consent (required for app permissions)
**Example permissions:**
- `User.Read.All` (application) - Read all users
- `Directory.Read.All` (application) - Read directory
#### 2. Request Access Token
```http
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id={application_id}
&scope=https://graph.microsoft.com/.default
&client_secret={client_secret}
&grant_type=client_credentials
```
**Parameters:**
- `scope`: Use `{resource}/.default` format
- For Microsoft Graph: `https://graph.microsoft.com/.default`
- For your API: `api://{api_app_id}/.default`
**Response:**
```json
{
"access_token": "eyJ0eXAi...",
"token_type": "Bearer",
"expires_in": 3599
}
```
#### 3. Use Access Token
```http
GET https://graph.microsoft.com/v1.0/users
Authorization: Bearer {access_token}
```
## Device Code Flow
**Use for:** Devices without browsers (IoT, CLIs), headless environments
### Flow Steps
```
1. App → Entra ID: Request device code
2. Entra ID → App: Device code + user code + verification URL
3. App → User: Display code and URL
4. User: Opens URL on another device, enters code
5. User → Entra ID: Authenticates & consents
6. App → Entra ID: Poll for token
7. Entra ID → App: Access token (after user completes auth)
```
### Implementation Steps
#### 1. Request Device Code
```http
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/devicecode
Content-Type: application/x-www-form-urlencoded
client_id={application_id}
&scope={scopes}
```
**Response:**
```json
{
"user_code": "GTHK-QPMN",
"device_code": "GMMhmHCXhWEzkobqIHGG_EnNYYsAkukHspeYUk9E8",
"verification_uri": "https://microsoft.com/devicelogin",
"expires_in": 900,
"interval": 5,
"message": "To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code GTHK-QPMN to authenticate."
}
```
#### 2. Display Instructions to User
```
To sign in, open https://microsoft.com/devicelogin
and enter code: GTHK-QPMN
```
#### 3. Poll for Token
```http
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id={application_id}
&grant_type=urn:ietf:params:oauth:grant-type:device_code
&device_code={device_code}
```
**Poll every 5 seconds (use `interval` from response)**
**Pending Response (user hasn't completed auth yet):**
```json
{
"error": "authorization_pending",
"error_description": "AADSTS70016: Pending end-user authorization..."
}
```
**Success Response:**
```json
{
"access_token": "eyJ0eXAi...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "M.R3_BAY...",
"id_token": "eyJ0eXAi..."
}
```
## Refresh Token Flow
**Use for:** Refreshing expired access tokens without re-authentication
### When to Refresh
- Access tokens typically expire in 1 hour
- Refresh tokens are long-lived (14-90 days)
- Refresh before access token expires for seamless UX
### Implementation
```http
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id={application_id}
&scope={scopes}
&refresh_token={refresh_token}
&grant_type=refresh_token
&client_secret={client_secret}
```
**Note:** `client_secret` only required for confidential clients
**Response:**
```json
{
"access_token": "eyJ0eXAi...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "M.R3_BAY...",
"id_token": "eyJ0eXAi..."
}
```
**Important:** New refresh token is returned; use it for next refresh
## Token Types
### Access Token
- Used to call APIs
- Contains claims (user ID, permissions, etc.)
- Short-lived (typically 1 hour)
- Format: JWT (JSON Web Token)
**Sample claims:**
```json
{
"aud": "https://graph.microsoft.com",
"iss": "https://sts.windows.net/{tenant}/",
"sub": "{user_object_id}",
"scp": "User.Read Mail.Read",
"exp": 1680000000
}
```
### Refresh Token
- Used to get new access tokens
- Long-lived (days to months)
- Opaque string (not JWT)
- Single-use (new one issued with each refresh)
### ID Token
- Contains user identity information
- Used by the app to authenticate user
- Format: JWT
**Sample claims:**
```json
{
"sub": "{user_object_id}",
"name": "Jane Doe",
"preferred_username": "[email protected]",
"email": "[email protected]",
"oid": "{object_id}"
}
```
## Scopes and Permissions
### Scope Format
**Microsoft Graph:**
```
https://graph.microsoft.com/User.Read
https://graph.microsoft.com/Mail.Send
```
**Custom API:**
```
api://{api_application_id}/access_as_user
```
## Security Considerations
| Practice | Why |
|----------|-----|
| **Use state parameter** | Prevents CSRF attacks |
| **Use PKCE for public clients** | Prevents authorization code interception |
| **Validate tokens** | Verify signature, issuer, audience, expiration |
| **Use HTTPS only** | Protect tokens in transit |
| **Store tokens securely** | Use secure storage, never in localStorage for sensitive apps |
| **Implement token refresh** | Seamless UX without repeated logins |
| **Handle token expiration** | Gracefully refresh or re-authenticate |
| **Minimal scope principle** | Request only necessary permissions |
## Additional Resources
[OAuth 2.0 spec](https://www.rfc-editor.org/rfc/rfc6749)
```
### references/console-app-example.md
```markdown
# Console Application Examples
This document provides complete working examples of console applications that authenticate with Microsoft Entra ID using MSAL (Microsoft Authentication Library).
## Table of Contents
- [C# (.NET) Example](#c-net-example)
- [Python Example](#python-example)
- [JavaScript (Node.js) Example](#javascript-nodejs-example)
## C# (.NET) Example
### Prerequisites
```bash
dotnet new console -n EntraAuthConsole
cd EntraAuthConsole
dotnet add package Microsoft.Identity.Client
```
### Complete Code
```csharp
using Microsoft.Identity.Client;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace EntraAuthConsole
{
class Program
{
// Configuration - replace with your values
private const string ClientId = "YOUR_APPLICATION_CLIENT_ID";
private const string TenantId = "YOUR_TENANT_ID";
private static readonly string[] Scopes = new[] { "User.Read" };
static async Task Main(string[] args)
{
try
{
// Build the MSAL client
var app = PublicClientApplicationBuilder
.Create(ClientId)
.WithAuthority(AzureCloudInstance.AzurePublic, TenantId)
.WithRedirectUri("http://localhost")
.Build();
// Try to get token silently from cache first
var accounts = await app.GetAccountsAsync();
AuthenticationResult result;
try
{
result = await app.AcquireTokenSilent(Scopes, accounts.FirstOrDefault())
.ExecuteAsync();
Console.WriteLine("Token acquired from cache");
}
catch (MsalUiRequiredException)
{
// Interactive authentication required
result = await app.AcquireTokenInteractive(Scopes)
.WithPrompt(Prompt.SelectAccount)
.ExecuteAsync();
Console.WriteLine("Token acquired interactively");
}
// Display user information
Console.WriteLine($"\nWelcome, {result.Account.Username}!");
Console.WriteLine($"Token expires: {result.ExpiresOn}");
// Call Microsoft Graph API
await CallGraphApiAsync(result.AccessToken);
}
catch (MsalException ex)
{
Console.WriteLine($"Error acquiring token: {ex.Message}");
}
}
private static async Task CallGraphApiAsync(string accessToken)
{
using var httpClient = new System.Net.Http.HttpClient();
httpClient.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
var response = await httpClient.GetAsync("https://graph.microsoft.com/v1.0/me");
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine("\nUser profile from Microsoft Graph:");
Console.WriteLine(content);
}
else
{
Console.WriteLine($"API call failed: {response.StatusCode}");
}
}
}
}
```
### Run the Application
```bash
dotnet run
```
### Device Code Flow (for headless scenarios)
```csharp
// Use this for servers or devices without a browser
result = await app.AcquireTokenWithDeviceCode(Scopes, deviceCodeResult =>
{
Console.WriteLine(deviceCodeResult.Message);
return Task.CompletedTask;
}).ExecuteAsync();
```
---
## Python Example
### Prerequisites
```bash
pip install msal requests
```
### Complete Code
```python
import msal
import requests
import json
# Configuration - replace with your values
CLIENT_ID = "YOUR_APPLICATION_CLIENT_ID"
TENANT_ID = "YOUR_TENANT_ID"
AUTHORITY = f"https://login.microsoftonline.com/{TENANT_ID}"
SCOPES = ["User.Read"]
def acquire_token_interactive():
"""Acquire token using interactive flow (opens browser)"""
app = msal.PublicClientApplication(
CLIENT_ID,
authority=AUTHORITY
)
# Try to get token from cache first
accounts = app.get_accounts()
result = None
if accounts:
# Try silent acquisition
result = app.acquire_token_silent(SCOPES, account=accounts[0])
if result:
print("Token acquired from cache")
if not result:
# Interactive authentication
result = app.acquire_token_interactive(
scopes=SCOPES,
prompt="select_account"
)
print("Token acquired interactively")
return result
def acquire_token_device_code():
"""Acquire token using device code flow (for headless scenarios)"""
app = msal.PublicClientApplication(
CLIENT_ID,
authority=AUTHORITY
)
flow = app.initiate_device_flow(scopes=SCOPES)
if "user_code" not in flow:
raise Exception(f"Failed to create device flow: {flow.get('error_description')}")
# Display instructions to user
print(flow["message"])
# Wait for user to complete authentication
result = app.acquire_token_by_device_flow(flow)
return result
def call_graph_api(access_token):
"""Call Microsoft Graph API with access token"""
headers = {
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json'
}
response = requests.get(
'https://graph.microsoft.com/v1.0/me',
headers=headers
)
if response.status_code == 200:
user_data = response.json()
print("\nUser profile from Microsoft Graph:")
print(json.dumps(user_data, indent=2))
else:
print(f"API call failed: {response.status_code}")
print(response.text)
def main():
# Choose authentication method
print("Select authentication method:")
print("1. Interactive (opens browser)")
print("2. Device code (for headless scenarios)")
choice = input("Enter choice (1 or 2): ")
try:
if choice == "1":
result = acquire_token_interactive()
elif choice == "2":
result = acquire_token_device_code()
else:
print("Invalid choice")
return
if "access_token" in result:
print(f"\nWelcome, {result.get('id_token_claims', {}).get('preferred_username', 'User')}!")
print(f"Token expires in: {result.get('expires_in')} seconds")
# Call Microsoft Graph API
call_graph_api(result["access_token"])
else:
print(f"Error acquiring token: {result.get('error')}")
print(f"Description: {result.get('error_description')}")
except Exception as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()
```
### Run the Application
```bash
python console_app.py
```
---
## JavaScript (Node.js) Example
### Prerequisites
```bash
npm init -y
npm install @azure/msal-node axios
```
### Complete Code
```javascript
const msal = require('@azure/msal-node');
const axios = require('axios');
// Configuration - replace with your values
const config = {
auth: {
clientId: "YOUR_APPLICATION_CLIENT_ID",
authority: "https://login.microsoftonline.com/YOUR_TENANT_ID",
}
};
const scopes = ["User.Read"];
// Interactive authentication (opens browser)
async function acquireTokenInteractive() {
const pca = new msal.PublicClientApplication(config);
const authCodeUrlParameters = {
scopes: scopes,
redirectUri: "http://localhost:3000",
};
// This opens the browser for authentication
const response = await pca.acquireTokenInteractive(authCodeUrlParameters);
return response;
}
// Device code flow (for headless scenarios)
async function acquireTokenDeviceCode() {
const pca = new msal.PublicClientApplication(config);
const deviceCodeRequest = {
deviceCodeCallback: (response) => {
console.log("\n" + response.message);
},
scopes: scopes,
};
const response = await pca.acquireTokenByDeviceCode(deviceCodeRequest);
return response;
}
// Client credentials flow (service-to-service, no user)
async function acquireTokenClientCredentials() {
const confidentialConfig = {
auth: {
clientId: "YOUR_APPLICATION_CLIENT_ID",
authority: "https://login.microsoftonline.com/YOUR_TENANT_ID",
clientSecret: "YOUR_CLIENT_SECRET", // From app registration
}
};
const cca = new msal.ConfidentialClientApplication(confidentialConfig);
const clientCredentialRequest = {
scopes: ["https://graph.microsoft.com/.default"],
};
const response = await cca.acquireTokenByClientCredential(clientCredentialRequest);
return response;
}
// Call Microsoft Graph API
async function callGraphApi(accessToken) {
const options = {
headers: {
Authorization: `Bearer ${accessToken}`
}
};
try {
const response = await axios.get('https://graph.microsoft.com/v1.0/me', options);
console.log('\nUser profile from Microsoft Graph:');
console.log(JSON.stringify(response.data, null, 2));
} catch (error) {
console.error('API call failed:', error.response?.status, error.message);
}
}
// Main function
async function main() {
console.log("Select authentication method:");
console.log("1. Device code flow (recommended for CLI)");
console.log("2. Client credentials (service-to-service)");
// For demonstration, using device code flow
// In production, get user input with readline or similar
const choice = "1";
try {
let result;
if (choice === "1") {
result = await acquireTokenDeviceCode();
} else if (choice === "2") {
result = await acquireTokenClientCredentials();
}
if (result.accessToken) {
console.log('\nAuthentication successful!');
console.log(`Token expires: ${new Date(result.expiresOn)}`);
// Call Microsoft Graph API
await callGraphApi(result.accessToken);
} else {
console.error('Failed to acquire token');
}
} catch (error) {
console.error('Error:', error.message);
}
}
main();
```
### Run the Application
```bash
node console_app.js
```
## Next Steps
- Review [oauth-flows.md](oauth-flows.md) for flow details
- See [api-permissions.md](api-permissions.md) for permission setup
- Check [troubleshooting.md](troubleshooting.md) for common issues
## Additional Resources
- [MSAL Libraries](https://learn.microsoft.com/entra/msal/)
```
### references/first-app-registration.md
```markdown
# First App Registration - Step-by-Step Guide
This guide walks you through creating your first Microsoft Entra app registration from scratch.
## Overview
You'll learn how to:
1. Create an app registration in Azure Portal
2. Configure authentication settings
3. Add API permissions
4. Create client credentials
5. Test the authentication flow
## Prerequisites
- Azure subscription (free tier works)
- Azure Portal access: https://portal.azure.com
- Basic understanding of your application type (web, mobile, service)
## Step 1: Navigate to App Registrations
1. Open [Azure Portal](https://portal.azure.com)
2. Search for **"Microsoft Entra ID"**
3. In the left menu, click **"App registrations"**
4. Click **"+ New registration"** at the top
## Step 2: Register Your Application
You'll see a form with several fields:
### Application Name
- **What to enter:** A descriptive name for your app
- **Example:** "My First Console App" or "Product Inventory API"
- **Tip:** Use a name that clearly identifies the purpose
### Supported Account Types
Choose who can use your application:
| Option | When to Use |
|--------|-------------|
| **Accounts in this organizational directory only (Single tenant)** | Only users from the same tenant of this app registration need access |
| **Accounts in any organizational directory (Multi-tenant)** | Users from multiple organization tenants need access |
| **Accounts in any organizational directory + Personal Microsoft accounts** | Users from multiple organization tenants and MSA users need access |
| **Personal Microsoft accounts only** | Only MSA users need access |
**Note:** Once selected, users whose account type is not allowed will get errors when trying to get access token for the app registration.
### Redirect URI (optional)
The redirect URI is where authentication responses are sent.
**Platform:** Select the type:
- **Web** - Server-side web apps
- **Single-page application (SPA)** - React, Angular, Vue apps
- **Public client/native** - Mobile, desktop, console apps
**URI examples:**
- Web app: `https://localhost:5001/signin-oidc`
- SPA: `http://localhost:3000`
- Console/Desktop: `http://localhost`
**For your first app:** Select **"Public client/native"** and enter `http://localhost`
### Click "Register"
After clicking, you'll be redirected to your app's overview page.
## Step 3: Save Important Information
On the **Overview** page, you'll see critical information. **Copy and save these values:**
### Application (client) ID
- **What it is:** Unique identifier for your app
- **Format:** `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` (GUID)
- **When you need it:** Every time your app authenticates
- **Where to save:** Environment variables, configuration file
### Directory (tenant) ID
- **What it is:** Unique identifier for your Azure AD tenant
- **Format:** `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` (GUID)
- **When you need it:** Constructing authentication URLs
### Example values to save:
```bash
# Save these in a secure location
APPLICATION_CLIENT_ID="12345678-1234-1234-1234-123456789012"
TENANT_ID="87654321-4321-4321-4321-210987654321"
```
## Step 4: Configure Authentication (Optional)
Click **"Authentication"** in the left menu.
### Advanced Settings
**Allow public client flows:**
- **What it is:** Enables device code flow, resource owner password flow
- **For console apps:** Turn this **ON**
- **For web apps:** Keep **OFF**
### Supported account types
You can change this later if needed.
### Logout URL (optional)
Where to redirect users after logout.
**Click "Save"** at the top if you made changes.
## Step 5: Add API Permissions
Click **"API permissions"** in the left menu.
### Default Permission
You'll see one default permission:
- **Microsoft Graph → User.Read (Delegated)**
This allows your app to read the signed-in user's profile.
### Add More Permissions
1. Click **"+ Add a permission"**
2. Select **"Microsoft Graph"**
3. Choose **"Delegated permissions"** (for user context)
4. Search for and select permissions you need:
- **User.Read** - Read user profile (already added)
- **Mail.Read** - Read user's mail
- **Calendars.Read** - Read user's calendar
5. Click **"Add permissions"**
### Admin Consent
Some permissions require admin consent:
- If you're an admin: Click **"Grant admin consent for [Your Org]"**
- If you're not: Ask your admin to grant consent
**Status indicator:**
- ✅ Green checkmark = Granted
- ⚠️ Yellow warning = Not granted (may still work for user consent)
## Step 6: Create Client Secret (If Needed)
**Skip this if:** You're building a desktop/mobile/console app (public client)
**Do this if:** You're building a web app, API, or service (confidential client)
1. Click **"Certificates & secrets"** in the left menu
2. Click **"+ New client secret"**
3. Enter a description: "Development Secret"
4. Choose expiration:
- **Recommended for development:** 6 months
- **For production:** 12-24 months (set up rotation)
5. Click **"Add"**
**⚠️ CRITICAL:** Copy the secret **Value** immediately!
- It's only shown once
- You cannot retrieve it later
- If you lose it, create a new one
```bash
# Save this securely (example)
CLIENT_SECRET="abc123~defGHI456jklMNO789pqrSTU"
```
**Security tips:**
- Never commit secrets to source control
- Use Azure Key Vault for production
- Use environment variables for development
## Step 7: Test Your App Registration
### Option A: Quick Test with Azure CLI
```bash
# Set your values
CLIENT_ID="your-client-id-here"
TENANT_ID="your-tenant-id-here"
# Interactive login
az login --scope "https://graph.microsoft.com/.default"
# Get an access token
az account get-access-token --resource "https://graph.microsoft.com"
```
### Option B: Test with MSAL Library
See the complete code example in [console-app-example.md](console-app-example.md)
### Expected Results
**Success:**
- Browser opens for authentication (or device code shown)
- You authenticate with your Azure AD account
- Access token is returned
- You can call Microsoft Graph API
**Common first-time issues:**
- Redirect URI mismatch → Double-check URI in Authentication settings
- Insufficient permissions → Add required API permissions
- User consent required → Grant admin consent or user must consent
**Tip:** Once you get the access token, you can use [jwt.ms](https://jwt.ms) to decode it and inspect its claims.
## Step 8: Review Configuration
### Checklist
- ✅ App registered with clear name
- ✅ Application ID and Tenant ID saved securely
- ✅ Redirect URI configured correctly
- ✅ API permissions added
- ✅ Admin consent granted (if required)
- ✅ Client secret created and saved (if needed)
- ✅ Authentication tested successfully
## Next Steps
- In your client app, implement the OAuth flow to acquire access tokens for your app registration.
- In your server app, implement token validation to protect your resources.
## Troubleshooting
### Redirect URI mismatch"
**Solution:**
- Check Authentication → Redirect URIs
- Ensure exact match (case-sensitive, trailing slash matters)
- Ensure correct platform (Web vs SPA vs Public client)
### User consent required
**Solution:**
- Grant admin consent in API permissions
- Or have user consent during first login
## Additional Resources
- [Microsoft Entra ID Documentation](https://learn.microsoft.com/en-us/entra/identity-platform/)
```
### references/sdk/azure-identity-py.md
```markdown
# Authentication — Python SDK Quick Reference
> Condensed from **azure-identity-py**. Full patterns (async,
> ChainedTokenCredential, token caching, all credential types)
> in the **azure-identity-py** plugin skill if installed.
## Install
```bash
pip install azure-identity
```
## Quick Start
```python
from azure.identity import DefaultAzureCredential
credential = DefaultAzureCredential()
```
## Best Practices
- Use DefaultAzureCredential for code that runs locally and in Azure
- Never hardcode credentials — use environment variables or managed identity
- Prefer managed identity in production Azure deployments
- Use ChainedTokenCredential when you need a custom credential order
- Close async credentials explicitly or use context managers
- Set AZURE_CLIENT_ID env var for user-assigned managed identities
- Exclude unused credentials to speed up authentication
```
### references/sdk/azure-identity-dotnet.md
```markdown
# Authentication — .NET SDK Quick Reference
> Condensed from **azure-identity-dotnet**. Full patterns (ASP.NET DI,
> sovereign clouds, brokered auth, certificate credentials)
> in the **azure-identity-dotnet** plugin skill if installed.
## Install
dotnet add package Azure.Identity
## Quick Start
```csharp
using Azure.Identity;
var credential = new DefaultAzureCredential();
```
## Best Practices
- Use deterministic credentials in production (ManagedIdentityCredential, not DefaultAzureCredential)
- Reuse credential instances — single instance shared across clients
- Configure retry policies for credential operations
- Enable logging with AzureEventSourceListener for debugging auth issues
```
### references/sdk/azure-identity-ts.md
```markdown
# Authentication — TypeScript SDK Quick Reference
> Condensed from **azure-identity-ts**. Full patterns (sovereign clouds,
> device code flow, custom credentials, bearer token provider)
> in the **azure-identity-ts** plugin skill if installed.
## Install
npm install @azure/identity
## Quick Start
```typescript
import { DefaultAzureCredential } from "@azure/identity";
const credential = new DefaultAzureCredential();
```
## Best Practices
- Use DefaultAzureCredential — works in development (CLI) and production (managed identity)
- Never hardcode credentials — use environment variables or managed identity
- Prefer managed identity — no secrets to manage in production
- Scope credentials appropriately — use user-assigned identity for multi-tenant scenarios
- Handle token refresh — Azure SDK handles this automatically
- Use ChainedTokenCredential for custom fallback scenarios
```
### references/sdk/azure-identity-java.md
```markdown
# Authentication — Java SDK Quick Reference
> Condensed from **azure-identity-java**. Full patterns (workload identity,
> certificate auth, device code, sovereign clouds)
> in the **azure-identity-java** plugin skill if installed.
## Install
```xml
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>1.15.0</version>
</dependency>
```
## Quick Start
```java
import com.azure.identity.DefaultAzureCredentialBuilder;
var credential = new DefaultAzureCredentialBuilder().build();
```
## Best Practices
- Use DefaultAzureCredential — works seamlessly from dev to production
- Managed identity in production — no secrets to manage, automatic rotation
- Azure CLI for local dev — run `az login` before running your app
- Least privilege — grant only required permissions to service principals
- Token caching — enabled by default, reduces auth round-trips
- Environment variables — use for CI/CD, not hardcoded secrets
```
### references/sdk/azure-identity-rust.md
```markdown
# Authentication — Rust SDK Quick Reference
> Condensed from **azure-identity-rust**. Full patterns (ClientSecret,
> ClientCertificate, WorkloadIdentity, AzurePipelines credentials)
> in the **azure-identity-rust** plugin skill if installed.
## Install
cargo add azure_identity
## Quick Start
```rust
use azure_identity::DeveloperToolsCredential;
let credential = DeveloperToolsCredential::new(None)?;
```
## Best Practices
- Use DeveloperToolsCredential for local dev — automatically picks up Azure CLI
- Use ManagedIdentityCredential in production — no secrets to manage
- Clone credentials — credentials are Arc-wrapped and cheap to clone
- Reuse credential instances — same credential can be used with multiple clients
- Use tokio feature — `cargo add azure_identity --features tokio`
```
### references/sdk/azure-keyvault-py.md
```markdown
# Key Vault — Python SDK Quick Reference
> Condensed from **azure-keyvault-py**. Full patterns (async clients,
> cryptographic operations, certificate management, error handling)
> in the **azure-keyvault-py** plugin skill if installed.
## Install
pip install azure-keyvault-secrets azure-keyvault-keys azure-keyvault-certificates azure-identity
## Quick Start
```python
from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient
client = SecretClient(vault_url="https://<vault>.vault.azure.net/", credential=DefaultAzureCredential())
```
## Best Practices
- Use DefaultAzureCredential for authentication
- Use managed identity in Azure-hosted applications
- Enable soft-delete for recovery (enabled by default)
- Use RBAC over access policies for fine-grained control
- Rotate secrets regularly using versioning
- Use Key Vault references in App Service/Functions config
- Cache secrets appropriately to reduce API calls
- Use async clients for high-throughput scenarios
```
### references/sdk/azure-keyvault-secrets-ts.md
```markdown
# Key Vault Secrets — TypeScript SDK Quick Reference
> Condensed from **azure-keyvault-secrets-ts**. Full patterns (key rotation,
> cryptographic operations, backup/restore, wrap/unwrap)
> in the **azure-keyvault-secrets-ts** plugin skill if installed.
## Install
npm install @azure/keyvault-secrets @azure/identity
## Quick Start
```typescript
import { DefaultAzureCredential } from "@azure/identity";
import { SecretClient } from "@azure/keyvault-secrets";
const client = new SecretClient("https://<vault>.vault.azure.net", new DefaultAzureCredential());
```
## Best Practices
- Use DefaultAzureCredential — works across dev and production
- Enable soft-delete — required for production vaults
- Set expiration dates on both keys and secrets
- Use key rotation policies — automate key rotation
- Limit key operations — only grant needed operations (encrypt, sign, etc.)
- Browser not supported — these SDKs are Node.js only
```
### references/sdk/microsoft-azure-webjobs-extensions-authentication-events-dotnet.md
```markdown
# Authentication Events — .NET SDK Quick Reference
> Condensed from **microsoft-azure-webjobs-extensions-authentication-events-dotnet**.
> Full patterns (attribute collection, OTP customization, external data enrichment)
> in the source plugin skill if installed.
## Install
dotnet add package Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents
## Quick Start
```csharp
using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents;
using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.TokenIssuanceStart;
[FunctionName("OnTokenIssuanceStart")]
public static WebJobsAuthenticationEventResponse Run(
[WebJobsAuthenticationEventsTrigger] WebJobsTokenIssuanceStartRequest request,
ILogger log)
{
var response = new WebJobsTokenIssuanceStartResponse();
response.Actions.Add(new WebJobsProvideClaimsForToken
{
Claims = new Dictionary<string, string> { { "claim", "value" } }
});
return response;
}
```
## Best Practices
- Validate all inputs — never trust request data; validate before processing
- Handle errors gracefully — return appropriate error responses, don't throw
- Log correlation IDs — use CorrelationId for troubleshooting
- Keep functions fast — authentication events have timeout limits
- Use managed identity — access Azure resources securely
- Cache external data — avoid slow lookups on every request
- Test locally — use Azure Functions Core Tools with sample payloads
- Monitor with App Insights — track function execution and errors
```
### references/troubleshooting.md
```markdown
# Troubleshooting Microsoft Entra App Registration
This guide helps you diagnose and fix common issues with app registrations and authentication.
## Table of Contents
- [Authentication Errors](#authentication-errors)
- [Token Issues](#token-issues)
- [Permission Problems](#permission-problems)
- [Redirect URI Issues](#redirect-uri-issues)
- [Application Configuration](#application-configuration)
- [Debugging Tools](#debugging-tools)
## Authentication Errors
### Redirect URI Mismatch
**Error message:**
```
AADSTS50011: The redirect URI 'http://localhost:3000' specified in the request
does not match the redirect URIs configured for the application.
```
**Cause:** The redirect URI in your authentication request doesn't exactly match what's registered.
**Solutions:**
1. **Check exact match** (case-sensitive, trailing slash matters):
```
Registered: https://myapp.com/callback
Request: https://myapp.com/callback/ ❌ (trailing slash)
Request: https://MyApp.com/callback ❌ (case difference)
Request: https://myapp.com/callback ✅
```
2. **Add URI to app registration:**
```bash
# Portal: Authentication → Add redirect URI
# CLI:
az ad app update --id $APP_ID \
--web-redirect-uris "http://localhost:3000" "https://myapp.com/callback"
```
3. **Check platform type:**
- Web URIs go in "Web" platform
- SPA URIs go in "Single-page application"
- Desktop/mobile URIs go in "Public client/native"
### Invalid Client Secret
**Error message:**
```
AADSTS7000215: Invalid client secret provided.
Ensure the secret being sent in the request is the client secret value, not the client secret ID.
```
**Causes:**
- Client secret expired
- Wrong secret value (copied secret ID instead of value)
- Secret doesn't match app registration
**Solutions:**
1. **Check expiration:**
```bash
az ad app credential list --id $APP_ID
```
2. **Create new secret:**
```bash
az ad app credential reset --id $APP_ID --years 1
```
Copy the `password` value (not the `keyId`)
### User Consent Required
**Error message:**
```
AADSTS65001: The user or administrator has not consented to use the application
```
**Causes:**
- Application permissions require admin consent
- User hasn't consented to delegated permissions
- Consent was revoked
**Solutions:**
1. **Grant admin consent (if admin):**
```bash
az ad app permission admin-consent --id $APP_ID
```
2. **Request user consent (interactive flow):**
This requires the client app to have access to UI such as browser, terminal window, etc. Follow the best practices of your client app to implement the interactive flow.
3. **Check API permissions in portal:**
- Ensure permissions are added
- Look for green checkmarks (granted)
- Yellow warning means not granted
### Grant Declined
**Error message:**
```
AADSTS70000: The request was denied because one or more permissions have been declined
```
**Cause:** User or admin explicitly denied consent.
**Solutions:**
1. **Re-request with explanation:**
- Explain why permissions are needed
- Request only necessary permissions
2. **Check if admin consent is required:**
- Some organizations disable user consent
- Contact your admin to grant consent
3. **Reduce permission scope:**
- Request minimal permissions initially
- Use incremental consent for additional features
### Application Not Found
**Error message:**
```
AADSTS700016: Application with identifier '{app-id}' was not found in the directory
```
**Causes:**
- Wrong application ID
- Wrong tenant ID
- Service principal not created
- App in different tenant
**Solutions:**
1. **Verify application ID:**
```bash
az ad app list --display-name "MyApp" --query "[].{Name:displayName, AppId:appId}"
```
2. **Verify tenant ID:**
```bash
az account show --query tenantId -o tsv
```
### Application Doesn't have a Service Principal
**Error message:**
```
The app is trying to access a service 'your_app_id'(your_app_name) that your organization 'your_tenant_id' lacks a service principal for
```
**Causes:**
- Your tenant is not configured to automatically provision the service principal for app registrations in it.
**Solutions:**
1. **Create service principal:**
```bash
az ad sp create --id $APP_ID
```
### Missing Required Field
**Error message:**
```
AADSTS90014: The required field 'client_id' is missing from the request
```
This can happen if the client you are using isn't compatible with Entra. Consult the owner of your client app to see if it supports Entra.
## Token Issues
Unless the the access token is encrypted, you can decode and view its claims securely at https://jwt.ms. **Don't** use any other website to decode an access token. Compare the claims in the token with the app registration's configuration to identify issues.
## Debugging Tools
### JWT Token Decoder
**Tool:** https://jwt.ms
**How to use:**
1. Copy your access token
2. Paste into jwt.ms
3. Review claims:
- `aud` - Audience (should match your API)
- `iss` - Issuer (should be login.microsoftonline.com)
- `scp` - Delegated permissions
- `roles` - Application permissions
- `exp` - Expiration timestamp
- `oid` - User object ID
---
### Fiddler
**Use for:** Inspecting HTTP requests/responses
**What to check:**
- Authorization header format: `Bearer {token}`
- Token is being sent
- Response status codes and error messages
### Entra Sign-in Logs
**Access:** Azure Portal → Microsoft Entra ID → Sign-in logs
**What to check:**
- Failed sign-in attempts
- Error codes and messages
- User consent status
- Conditional Access policy failures
## Common Error Codes Reference
| Error Code | Meaning | Common Cause |
|------------|---------|--------------|
| AADSTS50011 | Redirect URI mismatch | URI not registered or doesn't match |
| AADSTS50020 | Invalid tenant | Wrong tenant in authority URL |
| AADSTS50034 | User not found | User doesn't exist in tenant |
| AADSTS50053 | Account locked | Too many failed attempts |
| AADSTS50055 | Password expired | User needs to reset password |
| AADSTS50057 | Account disabled | User account disabled |
| AADSTS50058 | Silent sign-in failed | Interactive auth required |
| AADSTS50059 | Tenant not found | Invalid tenant ID |
| AADSTS65001 | Consent required | User/admin hasn't consented |
| AADSTS70000 | Grant declined | User denied consent |
| AADSTS70001 | App disabled | App registration disabled |
| AADSTS700016 | App not found | Invalid app ID or wrong tenant |
| AADSTS7000215 | Invalid client secret | Wrong/expired secret |
| AADSTS90014 | Missing field | Required parameter not sent |
| AADSTS90072 | Consent needed | Admin consent required |
## Best Practices for Troubleshooting
### Systematic Approach
1. **Collect information:**
- Exact error message and code
- When it started happening
- What changed recently
- Environment (dev/test/prod)
2. **Check basics first:**
- App ID and tenant ID correct
- Permissions added and consented
- Redirect URIs configured
- Secrets/certificates valid
3. **Use debugging tools:**
- Decode tokens (jwt.ms)
- Check sign-in logs
- Enable MSAL logging
- Use network inspector
4. **Test incrementally:**
- Test with minimal permissions
- Add permissions one at a time
- Test different flows separately
## Getting Help
### Microsoft Resources
- [Microsoft Q&A](https://learn.microsoft.com/answers/)
- [Microsoft Identity Platform Documentation](https://learn.microsoft.com/entra/identity-platform/)
```