
Entra App Registration
- 494k installs
- 1.3k repo stars
- Updated July 30, 2026
- microsoft/azure-skills
entra-app-registration is a Claude agent skill that guides registering an app in Microsoft Entra ID and setting up OAuth 2.0 and MSAL authentication.
About
entra-app-registration is a Microsoft agent skill that guides registering an application in Microsoft Entra ID (formerly Azure AD), configuring OAuth 2.0 flows, requesting API permissions, and integrating MSAL. It walks through registering the app, setting redirect URIs, choosing delegated or application permissions, creating client secrets or certificates, and producing working MSAL code for console, web, SPA, and service apps. A developer uses it when adding Azure AD / Entra authentication or generating a service principal. It defers Azure RBAC role assignments and Key Vault secret audits to other skills.
- Microsoft Entra ID (Azure AD) app registration
- OAuth 2.0 flows and API permissions
- MSAL code for console, web, SPA, service
- Client secret, certificate, or federated identity
Entra App Registration by the numbers
- 493,690 all-time installs (skills.sh)
- +16,985 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #1 of 2,203 Security skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 2, 2026 (Skillselion catalog sync)
entra-app-registration capabilities & compatibility
Free MIT-licensed skill; requires an Azure AD/Entra tenant.
- Capabilities
- app registration · oauth setup · msal integration · service principal creation
- Works with
- azure
- Use cases
- security audit · api development
- Pricing
- Free
What entra-app-registration says it does
Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration.
App registrations allow applications to authenticate users and access Azure resources securely.
MSAL is the recommended library for integrating Microsoft identity platform.
npx skills add https://github.com/microsoft/azure-skills --skill entra-app-registrationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 494k |
|---|---|
| repo stars | ★ 1.3k |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 30, 2026 |
| Repository | microsoft/azure-skills ↗ |
How do I register my app in Entra ID and set up OAuth 2.0 authentication with the right API permissions?
api-development
Who is it for?
Developers adding Microsoft Entra ID / Azure AD authentication to an app, configuring OAuth permissions, or generating a service principal.
Skip if: Azure RBAC or role assignments (use azure-rbac), Key Vault secret audits (use azure-keyvault-expiration-audit), or general resource security guidance.
When should I use this skill?
The user asks to create an app registration, register an Azure AD app, configure OAuth, add API permissions, generate a service principal, or wire up MSAL.
What you get
A configured Entra ID app registration with redirect URIs, API permissions, credentials, and working MSAL code to paste into the app.
- An Entra ID app registration
- Configured OAuth permissions and credentials
- MSAL code snippet
By the numbers
- 5-step core workflow
- 3 credential types: secret, certificate, federated identity
- 6 az ad CLI commands documented
Files
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 IaC Method: See 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://localhostor 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 profileUser.ReadWrite.All- Read and write all usersDirectory.Read.All- Read directory dataMail.Send- Send mail as a user
Details: See 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 - OAuth 2.0 flow details
- 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
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
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)
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
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
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 | .NET | TypeScript | Java | Rust
- Key Vault (secrets): Python | TypeScript
- Auth Events: .NET
References
- OAuth Flows - Detailed OAuth 2.0 flow explanations
- CLI Commands - Azure CLI reference for app registrations
- Console App Example - Complete working examples
- First App Registration - Step-by-step guide for beginners
- API Permissions - Understanding and configuring permissions
- Troubleshooting - Common issues and solutions
External Resources
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/.defaultWhen 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 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
# 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 jsonGranting 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:
az ad app permission admin-consent --id $APP_IDVerifying Consent Status
Portal: Look for green checkmarks in "Status" column
CLI:
az ad app permission list --id $APP_IDLook 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-6bf408dba283Application 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-ec69d8559fc7Note: Permission IDs may change. Always verify against the official Microsoft Graph Permissions Reference for the most current values.
Additional Resources
Azure Authentication Best Practices
Source: Microsoft — Passwordless connections for Azure services and Azure Identity client libraries.
Golden Rule
Use managed identities and Azure RBAC in production. Reserve DefaultAzureCredential for local development only.
Authentication by Environment
| Environment | Recommended Credential | Why |
|---|---|---|
| Production (Azure-hosted) | ManagedIdentityCredential (system- or user-assigned) | No secrets to manage; auto-rotated by Azure |
| Production (on-premises) | ClientCertificateCredential or WorkloadIdentityCredential | Deterministic; no fallback chain overhead |
| CI/CD pipelines | AzurePipelinesCredential / WorkloadIdentityCredential | Scoped to pipeline identity |
| Local development | DefaultAzureCredential | Chains CLI, PowerShell, and VS Code credentials for convenience |
Why Not DefaultAzureCredential in Production?
1. Unpredictable fallback chain — walks through multiple credential types, adding latency and making failures harder to diagnose. 2. Broad surface area — checks environment variables, CLI tokens, and other sources that should not exist in production. 3. Non-deterministic — which credential actually authenticates depends on the environment, making behavior inconsistent across deployments. 4. Performance — each failed credential attempt adds network round-trips before falling back to the next.
Production Patterns
.NET
using Azure.Identity;
var credential = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT") == "Development"
? new DefaultAzureCredential() // local dev — uses CLI/VS credentials
: new ManagedIdentityCredential(); // production — deterministic, no fallback chain
// For user-assigned identity: new ManagedIdentityCredential("<client-id>")TypeScript / JavaScript
import { DefaultAzureCredential, ManagedIdentityCredential } from "@azure/identity";
const credential = process.env.NODE_ENV === "development"
? new DefaultAzureCredential() // local dev — uses CLI/VS credentials
: new ManagedIdentityCredential(); // production — deterministic, no fallback chain
// For user-assigned identity: new ManagedIdentityCredential("<client-id>")Python
import os
from azure.identity import DefaultAzureCredential, ManagedIdentityCredential
credential = (
DefaultAzureCredential() # local dev — uses CLI/VS credentials
if os.getenv("AZURE_FUNCTIONS_ENVIRONMENT") == "Development"
else ManagedIdentityCredential() # production — deterministic, no fallback chain
)
# For user-assigned identity: ManagedIdentityCredential(client_id="<client-id>")Java
import com.azure.identity.DefaultAzureCredentialBuilder;
import com.azure.identity.ManagedIdentityCredentialBuilder;
var credential = "Development".equals(System.getenv("AZURE_FUNCTIONS_ENVIRONMENT"))
? new DefaultAzureCredentialBuilder().build() // local dev — uses CLI/VS credentials
: new ManagedIdentityCredentialBuilder().build(); // production — deterministic, no fallback chain
// For user-assigned identity: new ManagedIdentityCredentialBuilder().clientId("<client-id>").build()Local Development Setup
DefaultAzureCredential is ideal for local dev because it automatically picks up credentials from developer tools:
1. Azure CLI — az login 2. Azure Developer CLI — azd auth login 3. Azure PowerShell — Connect-AzAccount 4. Visual Studio / VS Code — sign in via Azure extension
import { DefaultAzureCredential } from "@azure/identity";
// Local development only — uses CLI/PowerShell/VS Code credentials
const credential = new DefaultAzureCredential();Environment-Aware Pattern
Detect the runtime environment and select the appropriate credential. The key principle: use DefaultAzureCredential only when running locally, and a specific credential in production.
Tip: Azure Functions setsAZURE_FUNCTIONS_ENVIRONMENTto"Development"when running locally. For App Service or containers, use any environment variable you control (e.g.NODE_ENV,ASPNETCORE_ENVIRONMENT).
import { DefaultAzureCredential, ManagedIdentityCredential } from "@azure/identity";
function getCredential() {
if (process.env.NODE_ENV === "development") {
return new DefaultAzureCredential(); // picks up az login / VS Code creds
}
return process.env.AZURE_CLIENT_ID
? new ManagedIdentityCredential(process.env.AZURE_CLIENT_ID) // user-assigned
: new ManagedIdentityCredential(); // system-assigned
}Security Checklist
- [ ] Use managed identity for all Azure-hosted apps
- [ ] Never hardcode credentials, connection strings, or keys
- [ ] Apply least-privilege RBAC roles at the narrowest scope
- [ ] Use
ManagedIdentityCredential(notDefaultAzureCredential) in production - [ ] Store any required secrets in Azure Key Vault
- [ ] Rotate secrets and certificates on a schedule
- [ ] Enable Microsoft Defender for Cloud on production resources
Further Reading
// Bicep template for Microsoft Entra App Registration
// Requires: Bicep v0.21.1+ with Microsoft Graph extension enabled
extension 'br:mcr.microsoft.com/bicep/extensions/microsoftgraph/v1.0:1.0.0'
@description('Display name for the application')
param appDisplayName string = 'MyEntraApp'
@description('Sign-in audience for the application')
@allowed([
'AzureADMyOrg'
'AzureADMultipleOrgs'
'AzureADandPersonalMicrosoftAccount'
'PersonalMicrosoftAccount'
])
param signInAudience string = 'AzureADMyOrg'
@description('Redirect URIs for web application')
param webRedirectUris array = [
'https://localhost:5001/signin-oidc'
'https://myapp.azurewebsites.net/signin-oidc'
]
@description('Redirect URIs for single-page application')
param spaRedirectUris array = [
'http://localhost:3000'
'https://myapp.azurewebsites.net'
]
@description('Tags for the application')
param tags array = [
'Production'
'WebApp'
]
// App Registration
resource appRegistration 'Microsoft.Graph/applications@v1.0' = {
displayName: appDisplayName
uniqueName: toLower(replace(appDisplayName, ' ', '-'))
signInAudience: signInAudience
tags: tags
// Application identification
identifierUris: [
'api://${appDisplayName}'
]
// Web application settings
web: {
redirectUris: webRedirectUris
implicitGrantSettings: {
enableIdTokenIssuance: true
enableAccessTokenIssuance: false
}
homePageUrl: 'https://myapp.azurewebsites.net'
logoutUrl: 'https://myapp.azurewebsites.net/signout-oidc'
}
// Single-page application settings
spa: {
redirectUris: spaRedirectUris
}
// Public client (mobile/desktop) settings
publicClient: {
redirectUris: [
'http://localhost'
'myapp://auth'
'https://login.microsoftonline.com/common/oauth2/nativeclient'
]
}
// API definition (expose an API)
api: {
// Version of the access token affects the values present in the token claims
requestedAccessTokenVersion: 2
oauth2PermissionScopes: [
{
id: '00000000-0000-0000-0000-000000000001'
adminConsentDisplayName: 'Read user data'
adminConsentDescription: 'Allows the app to read user data on behalf of the signed-in user'
userConsentDisplayName: 'Read your data'
userConsentDescription: 'Allows the app to read your data'
value: 'User.Read'
type: 'User'
isEnabled: true
}
{
id: '00000000-0000-0000-0000-000000000002'
adminConsentDisplayName: 'Read and write user data'
adminConsentDescription: 'Allows the app to read and write user data on behalf of the signed-in user'
userConsentDisplayName: 'Read and write your data'
userConsentDescription: 'Allows the app to read and write your data'
value: 'User.ReadWrite'
type: 'User'
isEnabled: true
}
]
}
// App roles for authorization
appRoles: [
{
id: '00000000-0000-0000-0000-000000000010'
displayName: 'Admin'
description: 'Administrators can manage all aspects of the app'
value: 'Admin'
allowedMemberTypes: ['User', 'Application']
isEnabled: true
}
{
id: '00000000-0000-0000-0000-000000000011'
displayName: 'Reader'
description: 'Readers can view data but not modify'
value: 'Reader'
allowedMemberTypes: ['User']
isEnabled: true
}
]
// Required API permissions (Microsoft Graph)
requiredResourceAccess: [
{
// Microsoft Graph API
resourceAppId: '00000003-0000-0000-c000-000000000000'
resourceAccess: [
{
// User.Read - Delegated
id: 'e1fe6dd8-ba31-4d61-89e7-88639da4683d'
type: 'Scope'
}
{
// User.ReadBasic.All - Delegated
id: 'b340eb25-3456-403f-be2f-af7a0d370277'
type: 'Scope'
}
{
// Mail.Read - Delegated
id: '570282fd-fa5c-430d-a7fd-fc8dc98a9dca'
type: 'Scope'
}
{
// User.Read.All - Application
id: 'df021288-bdef-4463-88db-98f22de89214'
type: 'Role'
}
]
}
]
// Optional claims configuration
optionalClaims: {
idToken: [
{
name: 'email'
essential: false
}
{
name: 'upn'
essential: false
}
{
name: 'groups'
essential: false
}
]
accessToken: [
{
name: 'email'
essential: false
}
]
}
// Information URLs
info: {
marketingUrl: 'https://myapp.example.com'
privacyStatementUrl: 'https://myapp.example.com/privacy'
supportUrl: 'https://myapp.example.com/support'
termsOfServiceUrl: 'https://myapp.example.com/terms'
}
}
// Service Principal (Enterprise Application)
resource servicePrincipal 'Microsoft.Graph/servicePrincipals@v1.0' = {
appId: appRegistration.appId
displayName: appDisplayName
tags: [
'WindowsAzureActiveDirectoryIntegratedApp'
]
appRoleAssignmentRequired: false
preferredSingleSignOnMode: 'oidc'
}
// Outputs
output applicationId string = appRegistration.appId
output objectId string = appRegistration.id
output servicePrincipalId string = servicePrincipal.id
output identifierUri string = appRegistration.identifierUris[0]
Azure CLI Commands for App Registration
This document provides a comprehensive reference for managing Microsoft Entra app registrations using Azure CLI.
Prerequisites
# 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:
az ad app create --display-name "MyApplication"Web application with redirect URI:
az ad app create \
--display-name "MyWebApp" \
--web-redirect-uris "https://myapp.com/callback" \
--sign-in-audience "AzureADMyOrg"Single Page Application (SPA):
az ad app create \
--display-name "MySpaApp" \
--spa-redirect-uris "http://localhost:3000" \
--sign-in-audience "AzureADMyOrg"Public client (Desktop/Mobile app):
az ad app create \
--display-name "MyDesktopApp" \
--public-client-redirect-uris "http://localhost" \
--sign-in-audience "AzureADMyOrg"Multi-tenant application:
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
az ad app list --output tableList apps with custom query
# 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 tableGet app details
# 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
APP_ID=$(az ad app list --display-name "MyApp" --query "[0].appId" -o tsv)
echo "Application ID: $APP_ID"Get Object ID
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:
az ad app update --id $APP_ID \
--web-redirect-uris "https://myapp.com/callback" "https://myapp.com/auth"SPA:
az ad app update --id $APP_ID \
--spa-redirect-uris "http://localhost:3000" "http://localhost:5000"Public client:
az ad app update --id $APP_ID \
--public-client-redirect-uris "http://localhost" "myapp://auth"Client Credentials (Secrets & Certificates)
Create client secret
# 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:
{
"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
# List all credentials (secrets and certificates)
az ad app credential list --id $APP_IDDelete client secret
# 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
# 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:
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):
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):
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
# Grant admin consent for all permissions
az ad app permission admin-consent --id $APP_IDNote: Admin consent is required for application permissions and some delegated permissions.
List permissions
az ad app permission list --id $APP_IDDelete permission
# Remove specific permission
az ad app permission delete --id $APP_ID \
--api $GRAPH_RESOURCE_ID \
--permission-id $USER_READ_IDService Principal Management
Create service principal
# Create service principal for the app
az ad sp create --id $APP_IDList service principals
az ad sp list --display-name "MyApp"Get service principal details
az ad sp show --id $APP_IDDelete service principal
az ad sp delete --id $APP_IDApp Roles and Claims
Get app roles
az ad app show --id $APP_ID --query "appRoles"Get optional claims
az ad app show --id $APP_ID --query "optionalClaims"Owners
List app owners
az ad app owner list --id $APP_IDAdd owner
# Add user as owner
USER_OBJECT_ID=$(az ad user show --id "user@domain.com" --query "id" -o tsv)
az ad app owner add --id $APP_ID --owner-object-id $USER_OBJECT_IDRemove owner
az ad app owner remove --id $APP_ID --owner-object-id $USER_OBJECT_IDDelete App Registration
# Delete app registration (and associated service principal)
az ad app delete --id $APP_IDTenant and Identity Information
Get tenant ID
az account show --query tenantId -o tsvGet current user information
az ad signed-in-user showGet user by email
az ad user show --id "user@domain.com"Get user object ID
az ad user show --id "user@domain.com" --query "id" -o tsvList all users
az ad user list --output tableScripting Examples
Complete app setup script
#!/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
#!/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
doneConsole 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
Prerequisites
dotnet new console -n EntraAuthConsole
cd EntraAuthConsole
dotnet add package Microsoft.Identity.ClientComplete Code
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
dotnet runDevice Code Flow (for headless scenarios)
// 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
pip install msal requestsComplete Code
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
python console_app.py---
JavaScript (Node.js) Example
Prerequisites
npm init -y
npm install @azure/msal-node axiosComplete Code
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
node console_app.jsNext Steps
- Review oauth-flows.md for flow details
- See api-permissions.md for permission setup
- Check troubleshooting.md for common issues
Additional Resources
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 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:
# 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
# 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
# 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
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 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
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 tokenImplementation 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 orcommonfor multi-tenantclient_id: Application (client) ID from app registrationredirect_uri: Must match exactly what's registeredscope: 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
statematches what you sent - Extract
codeparameter
4. Exchange Code for Tokens
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:
{
"access_token": "eyJ0eXAi...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "M.R3_BAY...",
"id_token": "eyJ0eXAi..."
}5. Use Access Token
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 tokenImplementation Steps
1. Generate PKCE Values
Code Verifier: 43-128 character random string
// JavaScript example
const codeVerifier = generateRandomString(128);Code Challenge: Base64URL-encoded SHA256 hash of verifier
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=S2563. Exchange Code for Tokens (No Secret)
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 tokenImplementation 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 usersDirectory.Read.All(application) - Read directory
2. Request Access Token
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_credentialsParameters:
scope: Use{resource}/.defaultformat- For Microsoft Graph:
https://graph.microsoft.com/.default - For your API:
api://{api_app_id}/.default
Response:
{
"access_token": "eyJ0eXAi...",
"token_type": "Bearer",
"expires_in": 3599
}3. Use Access Token
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
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/devicecode
Content-Type: application/x-www-form-urlencoded
client_id={application_id}
&scope={scopes}Response:
{
"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-QPMN3. Poll for Token
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):
{
"error": "authorization_pending",
"error_description": "AADSTS70016: Pending end-user authorization..."
}Success Response:
{
"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
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:
{
"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:
{
"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:
{
"sub": "{user_object_id}",
"name": "Jane Doe",
"preferred_username": "jane@contoso.com",
"email": "jane@contoso.com",
"oid": "{object_id}"
}Scopes and Permissions
Scope Format
Microsoft Graph:
https://graph.microsoft.com/User.Read
https://graph.microsoft.com/Mail.SendCustom API:
api://{api_application_id}/access_as_userSecurity 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
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
Auth: DefaultAzureCredential is for local development. See auth-best-practices.md for production patterns.using Azure.Identity;
var credential = new DefaultAzureCredential();Best Practices
- Use DefaultAzureCredential for local development only. In production, use deterministic credentials (ManagedIdentityCredential) — see auth-best-practices.md
- Reuse credential instances — single instance shared across clients
- Configure retry policies for credential operations
- Enable logging with AzureEventSourceListener for debugging auth issues
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
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>1.15.0</version>
</dependency>Quick Start
Auth: DefaultAzureCredential is for local development. See auth-best-practices.md for production patterns.import com.azure.identity.DefaultAzureCredentialBuilder;
var credential = new DefaultAzureCredentialBuilder().build();Best Practices
- Use DefaultAzureCredential for local development only (CLI, PowerShell, VS Code). In production, use ManagedIdentityCredential — see auth-best-practices.md
- Managed identity in production — no secrets to manage, automatic rotation
- Azure CLI for local dev — run
az loginbefore 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
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
pip install azure-identityQuick Start
Auth: DefaultAzureCredential is for local development. See auth-best-practices.md for production patterns.from azure.identity import DefaultAzureCredential
credential = DefaultAzureCredential()Best Practices
- Use DefaultAzureCredential for local development only (CLI, PowerShell, VS Code). In production, use ManagedIdentityCredential — see auth-best-practices.md
- 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
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
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
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
Auth: DefaultAzureCredential is for local development. See auth-best-practices.md for production patterns.import { DefaultAzureCredential } from "@azure/identity";
const credential = new DefaultAzureCredential();Best Practices
- Use DefaultAzureCredential for local development only (CLI, PowerShell, VS Code). In production, use ManagedIdentityCredential — see auth-best-practices.md
- 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
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
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 local development only. In production, use ManagedIdentityCredential — see auth-best-practices.md
- 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
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
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 for local development only. In production, use ManagedIdentityCredential — see auth-best-practices.md
- 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
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
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
Troubleshooting Microsoft Entra App Registration
This guide helps you diagnose and fix common issues with app registrations and authentication.
Table of Contents
- Authentication Errors
- Token Issues
- Permission Problems
- Redirect URI Issues
- Application Configuration
- 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:
# 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:
az ad app credential list --id $APP_ID2. Create new secret:
az ad app credential reset --id $APP_ID --years 1Copy the password value (not the keyId)
User Consent Required
Error message:
AADSTS65001: The user or administrator has not consented to use the applicationCauses:
- Application permissions require admin consent
- User hasn't consented to delegated permissions
- Consent was revoked
Solutions:
1. Grant admin consent (if admin):
az ad app permission admin-consent --id $APP_ID2. 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 declinedCause: 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 directoryCauses:
- Wrong application ID
- Wrong tenant ID
- Service principal not created
- App in different tenant
Solutions:
1. Verify application ID:
az ad app list --display-name "MyApp" --query "[].{Name:displayName, AppId:appId}"2. Verify tenant ID:
az account show --query tenantId -o tsvApplication 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 forCauses:
- Your tenant is not configured to automatically provision the service principal for app registrations in it.
Solutions:
1. Create service principal:
az ad sp create --id $APP_IDMissing Required Field
Error message:
AADSTS90014: The required field 'client_id' is missing from the requestThis 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 permissionsroles- Application permissionsexp- Expiration timestampoid- 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
Related skills
How it compares
Use entra-app-registration for application identity and OAuth; use azure-rbac for resource-level role assignments on Azure services.
FAQ
What does entra-app-registration set up?
It guides Microsoft Entra ID app registration, OAuth 2.0 authentication, API permission selection, and MSAL integration for console and web apps.
Which credential types does it cover?
Client secrets, certificates, and federated identity credentials for confidential client applications, with Key Vault recommended for storage.
Does it handle Azure RBAC roles?
No; it defers Azure RBAC and role assignments to azure-rbac and Key Vault secret audits to azure-keyvault-expiration-audit.
Is Entra App Registration safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.