Implements OAuth 2.0 authorization protocol, allowing third-party apps to access web-hosted resources on behalf of users. These resources have a unique application ID URI.
Feature | Entra ID | Azure RBAC |
---|---|---|
Purpose | Authentication. Identity & Access Management | Authorization |
Focus | Entra ID Resources | Azure Resources |
Scope | Tenant | Management Group, Subscription, Resource Group, Resource |
Roles | Global, User, Billing Admins; Custom roles; Multiple roles per user | Owner, Contributor, Reader, User Access Admin; Custom roles (P1/P2); Multiple roles per user |
Access Via | Azure Portal, MS 365 Admin, Graph, PowerShell | Azure Portal, CLI, PowerShell, ARM templates, REST API |
Pricing | Free, P1, P2 (Monthly charged) | Free (With Azure subscription) |
- For multi-tenant applications, an application service principal is utilized to grant permissions. This enables the app to access Microsoft Graph API resources across multiple tenants without relying on a specific user identity.
- For single-tenant scenarios where the app only needs to access resources within a specific tenant, a Managed Identity could be more appropriate.
- When the app needs to perform actions specific to an individual user (on behalf of the user), delegated permissions are used, requiring user authentication and consent.
- If the permissions required by the application can change dynamically based on runtime conditions, leveraging Entra ID roles and policies would be more suitable.
- For short-lived operations that don't require persistent permissions, token-based or key-based temporary access methods could be more fitting.
All applications must register with Entra ID to delegate identity and access management: Portal > app > 'Authentication' > 'Add identity provider' > set provider to Microsoft > 'Add'
. This creates an application object and a globally unique ID (app/client ID).
-
Application Object: Resides in the Entra ID tenant where the app is registered. It serves as the global representation of your application for use across all tenants. This object has:
- A 1:1 relationship with the Software Application.
- A 1:N relationship with Service Principal Objects, meaning one Application Object can have multiple corresponding Service Principal Objects.
-
Service principals Objects: These are local representations within each tenant (use Enterprise applications page in the Azure portal to manage). They are derived from the Application Object and come in three types:
- Application: Created when an app gains resource access permissions.
- Managed Identity: Automatically created when enabled. It grants access but is not directly modifiable.
- Legacy: For apps created before modern registration methods, restricted to the tenant where created.
Changes to your application object also affect its service principals in the home tenant only. Deleting the application also deletes its home tenant service principal, but restoring that application object won't recover its service principals.
List service principals associated with an app: az ad sp list --filter "appId eq '{AppId}'"
Integrate authentication and authorization | Web App | Backend API | Daemon |
---|---|---|---|
1. Register in Entra ID | ✓ | ✓ | ✓ |
2. Configure app with code sample | ✕ | ✓ | ✕ |
3. Validate token | ID | Access | ✕ |
4. Configure secrets & certificates | ✓ | ✓ | ✓ |
5. Configure permission & call API of choice | ✓ | ✓ | ✓ |
6. Control access (authorization) | ✓ | ✓ (add validate-jwt policy to validate the OAuth token) |
✕ |
7. Store token cache | ✓ | ✓ | ✓ |
To protect an API in Azure API Management, register both the backend API and web app, configure permissions to allow the web app to call the backend API (az ad app permission add --id <WebApp-Application-Id> --api <Backend-API-Application-Id> --api-permissions <Permission-Id>=Scope
), and enable OAuth 2.0 user authorization along with adding the validate-jwt
policy for token validation.
<Permission-Id>=Scope
: Delegated permissions<Permission-Id>=Role
: Application permissions
The app specifies required permissions using the scope
query parameter, which defines the resource type. If unspecified, the default resource is Microsoft Graph. For instance, scope=User.Read
is the same as https://graph.microsoft.com/User.Read
.
Permission types | Delegated permissions | Application permissions |
---|---|---|
Access context | Get access on behalf of a user (a signed-in user is present) | Get access without a user (signed-in user) |
Types of apps | Web / Mobile / single-page app (SPA) | Web / Daemon / Background services |
Other names | Scopes / OAuth2 permission scopes | App / App-only permissions roles |
Who can consent | - Users can consent for their data - Admins can consent for all users |
Only admin can consent |
Consent methods | Static or Dynamic | Static ONLY |
az ad app permission add --id {appId} --api {apiID} --api-permissions {permissionId}={Scope,Role}
- Scope: Defines what the application is allowed to do on behalf of a user.
- Role: Defines more privileged operations that the application can perform, often without a user
- Static user consent: Requires all permissions to be specified in the Azure portal during app registration. Users or admins are prompted for consent if not previously granted. Issues: requires long lists of permissions and knowing all resources in advance.
- Incremental (Dynamic) user consent: Allows permissions to be requested gradually. Scopes can be specified during runtime without predefinition in Azure portal.
- Admin consent: needed for high-privilege permissions. Admins authorize apps to access privileged data. Requires static permissions registration.
az ad app permission admin-consent
Requesting individual user consent:
GET https://login.microsoftonline.com/common/oauth2/v2.0/authorize?
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&response_type=code
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&response_mode=query
&scope=https%3A%2F%2Fgraph.microsoft.com%2Fcalendars.read%20https%3A%2F%2Fgraph.microsoft.com%2Fmail.send
&state=12345
Conditional Access (Premium P1 tier)
- Prompt additional verification (e.g., second password or fingerprint) when users sign in
- Using a middle tier to solve a "challenge" presented by API
- Multi-Factor Authentication (all Microsoft 365 plans). When Security Defaults is enabled, MFA is activated for all users. To apply MFA to specific users only, disable Security Defaults.
- Risk-based policies (require Entra ID Identity Protection - Premium P2 tier)
- Device restrictions (enrolled in Microsoft's Intune service)
- Certain physical locations or IP ranges
When Conditional Access licenses expire, policies stay active but can't be updated.
Apps don't need to be changed, unless they need silent or indirect services access, or on-behalf-of flow.
- Entra ID B2C supports multiple login methods, including social media, email/password.
- Entra ID B2B allows you to share your company's applications with external users in a secure manner.
- Entra ID Application Proxy provides secure remote access to on-premises applications.
- Entra ID Connect allows you to synchronize an AD tenant with an on-premises AD domain.
- Entra ID Enterprise Application allow you to integrate other applications with Entra ID, including your own apps.
Enables secure access to various APIs, with a unified API across platforms.
- Obtains tokens for users or applications (when applicable).
- Manages token cache and refreshes tokens automatically.
- Helps specify the desired audience for application sign-in.
- Assists with application setup from configuration files.
- Provides actionable exceptions, logging, and telemetry for troubleshooting.
Flow | Application Type | Use Case | Token Acquisition Method |
---|---|---|---|
Authorization code | Both | SPA, Native, Web Apps | Exchanges an authorization code for a token |
Client credentials | Confidential | Daemon, Backend | App Secret/Certificate |
On-behalf-of | Both | Service-to-Service, Service-to-API, Microservices | Uses an existing token to get another |
Device code | Public | IoT, CLI | Polls the endpoint until user authenticates |
Implicit | Public | Legacy SPAs | Token in URI fragment |
Integrated Windows | Both | Intranet Apps | Auto auth on domain / Entra ID-joined PCs |
Interactive | Public | User Interactive Apps | Requires user action |
Username/password | Both | Legacy, Testing | Direct with Credentials |
- Public client applications: User-facing apps without the ability to securely store secrets. They interact with web APIs on the user's behalf.
- Confidential client applications: Server-based apps and daemons that can securely handle secrets. Each instance maintains a unique configuration, including identifiers and secrets.
When building web apps or public client apps that require a broker, ensure to set the redirectUri
. This URL is used by the identity provider to return security tokens to your application.
Integrating Entra ID authentication into an ASP.NET Core application:
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddRazorPages()
.AddMicrosoftIdentityUI();
// OpenIdConnectDefaults.AuthenticationScheme: Enables OpenID Connect authentication, best for OAuth 2.0 and Single Sign-On (SSO).
// JwtBearerDefaults.AuthenticationScheme: Used for authenticating APIs via JSON Web Tokens (JWT), suitable for stateless and scalable APIs.
// CookieAuthenticationDefaults.AuthenticationScheme: Employs cookies for session-based authentication, optimal for traditional web apps that manage user sessions server-side.
// Custom Authentication Scheme: Allows for custom string identifiers for authentication middleware, ideal for specialized or unique authentication scenarios.
// Sign in users in the Microsoft Azure public cloud using their work and school accounts or personal Microsoft accounts.
IPublicClientApplication app = PublicClientApplicationBuilder.Create(clientId).Build();
// Confidential application that handles tokens from Microsoft Azure users using a shared client secret for identification.
// Example: A daemon application that does not interact with the user and acts on its own behalf, like a service accessing Graph API
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithRedirectUri(redirectUri )
.Build();
Common modifiers:
Modifier | Description |
---|---|
.WithAuthority() |
Sets the application default authority to an Microsoft Entra ID authority, with the possibility of choosing the Azure Cloud, the audience, the tenant (tenant ID or domain name), or providing directly the authority URI. Example: .WithAuthority(AzureCloudInstance.AzurePublic, _tenantId) |
.WithTenantId(string tenantId) |
Overrides the tenant ID, or the tenant description. |
.WithClientId(string) |
Overrides the client ID. |
.WithRedirectUri(string redirectUri) |
Overrides the default redirect URI (ex: for scenarios requiring a broker) |
.WithComponent(string) |
Sets the name of the library using MSAL.NET (for telemetry reasons). |
.WithDebugLoggingCallback() |
If called, the application calls Debug.Write simply enabling debugging traces. |
.WithLogging() |
If called, the application calls a callback with debugging traces. |
.WithTelemetry(TelemetryCallback telemetryCallback) |
Sets the delegate used to send telemetry. |
Confidential client application only:
Modifier | Description |
---|---|
.WithCertificate(X509Certificate2 certificate) |
Sets the certificate identifying the application with Microsoft Entra ID. |
.WithClientSecret(string clientSecret) |
Sets the client secret (app password) identifying the application with Microsoft Entra ID. |
Acquiring Token:
string[] scopes = { "user.read" };
AuthenticationResult result = await app.AcquireTokenInteractive(scopes).ExecuteAsync();
Console.WriteLine($"Token: {result.AccessToken}");
An Entra ID application manifest configures an app's identity and attributes, facilitating OAuth authorization and user consent. It serves as a mechanism for updating the application object in the Microsoft identity platform.
-
signInAudience
:AzureADMyOrg
- Users with a Microsoft work or school account in my organization's Microsoft Entra tenant (for example, single tenant)AzureADMultipleOrgs
- Users with a Microsoft work or school account in any organization's Microsoft Entra tenant (for example, multi-tenant)AzureADandPersonalMicrosoftAccount
- Users with a personal Microsoft account, or a work or school account in any organization's Microsoft Entra tenantPersonalMicrosoftAccount
- Personal accounts that are used to sign in to services like Xbox and Skype.
-
groupMembershipClaims
: (Tenant-specific) Groups claim issued in access token that the app expects. Groups persist even after the associated app is removed.- "None"
- "SecurityGroup" (will include security groups and Entra ID roles)
- "ApplicationGroup" (this option includes only groups that are assigned to the application)
- "DirectoryRole" (gets the Entra ID directory roles the user is a member of)
- "All" (this will get all of the security groups, distribution groups, and Entra ID directory roles that the signed-in user is a member of).
-
appRoles
(Application-specific): Collection of roles that an app may declare. Defined in the app registration, and will get removed with it. Correspond toRole
in--api-permissions
"appRoles": [{ "allowedMemberTypes": [ "User" ], "value": "ReadOnly" // expected value of the roles claim in the token, which must match the string in the application's code without spaces. }]
-
oauth2Permissions
: Specifies the collection of OAuth 2.0 permission scopes that the web API (resource) app exposes to client apps. Correspond toScope
in--api-permissions
. -
oauth2AllowImplicitFlow
- If the web app can request implicit flow access tokens (oauth2AllowIdTokenImplicitFlow
for ID tokens). ⭐: SPAs, when using Implicit Grant Flow.
Attribute Name | Brief Explanation |
---|---|
requiredResourceAccess |
Specifies the resources that the app requires access to. |
keyCredentials |
Holds references to app-assigned credentials, string-based shared secrets and X.509 certificates. |
acceptMappedClaims |
Allows an application to use claims mapping without specifying a custom signing key. |
optionalClaims |
The optional claims returned in the token by the security token service for this specific app. |
addIns |
Defines custom behavior that a consuming service can use to call an app in specific contexts. |
allowPublicClient |
Specifies the fallback application type. |
knownClientApplications |
Used for bundling consent if you have a solution that contains two parts: a client app and a custom web API app. |
oauth2RequirePostResponse |
Specifies whether, as part of OAuth 2.0 token requests, Entra ID will allow POST requests, as opposed to GET requests. |
passwordCredentials |
Similar to keyCredentials , holds references to app-assigned credentials, string-based shared secrets. |
preAuthorizedApplications |
Lists applications and requested permissions for implicit consent. |
replyUrlsWithType |
Holds the list of registered redirect_uri values that Entra ID will accept as destinations when returning tokens. |
signInAudience |
Specifies what Microsoft accounts are supported for the current application. |
identifierUris |
User-defined URI(s) that uniquely identify a web app within its Entra ID tenant or verified customer owned domain. |
tags |
Custom strings that can be used to categorize and identify the application. |
parentalControlSettings |
Specifies the countries/regions in which the app is blocked for minors and the legal age group rule that applies to users of the app. |
accessTokenAcceptedVersion |
Specifies the access token version expected by the resource. |
logoutUrl |
The URL to log out of the app. |
signInUrl |
Specifies the URL to the app's home page. |
logoUrl |
Read only value that points to the CDN URL to logo that was uploaded in the portal. |
samlMetadataUrl |
The URL to the SAML metadata for the app. |
publisherDomain |
The verified publisher domain for the application. |
informationalUrls |
Specifies the links to the app's terms of service and privacy statement. |
appId |
Specifies the unique identifier for the app that is assigned to an app by Entra ID. |
name |
The display name for the app. |
id |
The unique identifier for the app in the directory. |
- Policies: A policy is a function that can look at a user's identity and decide whether they are authorized to perform a given action.
- Roles: A role represents a group of users that have certain privileges as defined by the role.
- Claims: A claim is a name-value pair that represents what the subject is, not what the subject can do.
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Define policies
services.AddAuthorization(options =>
{
options.AddPolicy("ClientsOnly", policy =>
{
policy.RequireAuthenticatedUser(); // Requires the user to be authenticated
policy.RequireRoles("PrivateClient", "CorporateClient"); // Requires ANY of these listed roles
policy.RequireClaim("SubscriptionTier", "free", "basic", "premium"); // Requires ANY of the values for SubscriptionTier
});
options.AddPolicy("FreeloadersOnly", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireRole("PrivateClient");
policy.RequireClaim("SubscriptionTier", "free");
});
options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber")); // Requires EmployeeNumber claim
options.AddPolicy("AdminOnly", policy => policy.RequireRole("Administrator")); // Requires Administrator role
});
}
// LoginController.cs
[HttpPost]
public async Task<IActionResult> Login(LoginViewModel model)
{
// Checks omitted for brevity
var user = await _userManager.FindByNameAsync(model.Username);
var claim = new Claim("EmployeeNumber", "123");
await _userManager.AddClaimAsync(user, claim);
}
[Authorize(Policy = "ClientsOnly")] // Allow premium clients only
public class AdminController : Controller { }
[Authorize(Role = "CorporateClient")] // Allow corporate clients only
public class AdminController : Controller { }
[Authorize(Policy = "EmployeeOnly")] // Apply EmployeeOnly policy
public class WorkController : Controller { }
[Authorize(Policy = "AdminOnly")] // Apply AdminOnly policy
public class AdminController : Controller { }
[Authorize(Policy = "ClientsOnly")] // Clients only that also have "Administrator" role (AND)
[Authorize(Roles = "Administrator")]
public class ClientAdminController : Controller { }