AZ-204Chapter 22 of 102Objective 3.1

OAuth 2.0 Flows and MSAL Library

This chapter covers OAuth 2.0 authorization flows and the Microsoft Authentication Library (MSAL) for Azure identity management. It is a critical topic for the AZ-204 exam, appearing in roughly 15-20% of questions related to security (objective 3.1). You will learn the four main OAuth 2.0 grant types, how MSAL abstracts them, and exactly what the exam tests—including common pitfalls like token expiration, scope misuse, and incorrect flow selection. Master these concepts to securely delegate access in Azure applications.

25 min read
Intermediate
Updated May 31, 2026

The Hotel Key Card Analogy

Imagine a hotel with multiple security zones: the lobby (public), the guest floors (protected), and the executive lounge (highly restricted). When you check in, the front desk gives you a key card that contains a digital token with your room number and a timestamp. This key card is like an access token. To enter your room, you swipe the card at the door; the door's reader verifies the card's signature (issued by the hotel's master system) and checks that the timestamp hasn't expired. The card does not contain your name or credit card info—it only proves you have permission to enter that specific room until checkout time. If you want to access the executive lounge, you cannot just show your room key; you need a separate authorization from the concierge, who issues a different card with lounge access. This is like OAuth 2.0: the front desk is the authorization server, you are the resource owner, the key card is the access token, and the room door is the resource server. The concierge issuing a lounge card is like using a refresh token to get a new access token with broader scope. Importantly, the key card never reveals your personal identity to the door—it only proves authorization. This separation of authentication (who you are) from authorization (what you can do) is the core mechanism of OAuth 2.0.

How It Actually Works

What is OAuth 2.0 and Why It Exists

OAuth 2.0 is an authorization framework defined in RFC 6749 that enables third-party applications to obtain limited access to an HTTP service on behalf of a resource owner. It separates authentication from authorization: the resource owner (user) authenticates to an authorization server, which issues an access token that the client application presents to the resource server. The token is a bearer token—any party in possession of it can use it. This is why tokens must be kept confidential and have limited lifetimes.

OAuth 2.0 Roles

Resource Owner: The user who owns the data (e.g., a Microsoft account holder).

Client: The application requesting access (e.g., a web app, mobile app, or daemon).

Authorization Server: The server that authenticates the resource owner and issues tokens (e.g., Azure AD).

Resource Server: The server hosting the protected resources (e.g., Microsoft Graph API).

OAuth 2.0 Grant Types (Flows)

The exam focuses on four main flows, each suited for different client types:

1.

Authorization Code Flow: Used by server-side web applications that can securely store a client secret. The client redirects the user to the authorization server, which authenticates the user and returns an authorization code. The client exchanges this code for an access token (and optionally a refresh token) using its client secret. This flow provides the highest security because the token is never exposed to the user's browser.

2.

Authorization Code Flow with PKCE (Proof Key for Code Exchange): An extension for public clients (mobile apps, single-page apps) that cannot securely store a client secret. PKCE uses a cryptographically random code verifier and a code challenge derived from it. The client sends the code challenge during the authorization request and the code verifier during the token exchange. The authorization server verifies that the verifier matches the challenge, preventing authorization code interception attacks.

3.

Client Credentials Flow: Used for server-to-server communication where the client is acting on its own behalf (no user). The client authenticates directly with its client ID and client secret (or certificate) and receives an access token. This flow is common for daemons, background services, and APIs that need to access resources without user interaction.

4.

On-Behalf-Of (OBO) Flow: Used when a middle-tier service (e.g., a web API) needs to call another downstream API on behalf of the user. The middle-tier receives an access token from the client, exchanges it for a new token (with appropriate scopes) to call the downstream API, and passes the user's identity. This flow is critical for building multi-tier applications.

MSAL (Microsoft Authentication Library)

MSAL is the Microsoft implementation of OAuth 2.0 and OpenID Connect for Azure AD. It abstracts the complexities of the OAuth 2.0 flows, token management, and caching. MSAL supports multiple platforms: .NET (MSAL.NET), JavaScript (MSAL.js), Python (MSAL Python), Java (MSAL4J), and others. On the exam, you are expected to know how to initialize MSAL, acquire tokens using the appropriate flow, and handle token caching and refresh.

Key MSAL Concepts

Public Client vs. Confidential Client: A public client cannot securely store secrets (e.g., mobile app, SPA). A confidential client can (e.g., web app with server-side code).

Application Registration: Each client must be registered in Azure AD, which provides a Client ID (Application ID). Confidential clients also have a Client Secret or Certificate.

Scopes: Permissions the client requests. For Microsoft Graph, typical scopes are User.Read, Mail.Send. For custom APIs, scopes are defined in the app registration.

Token Cache: MSAL automatically caches tokens to minimize authentication prompts. The cache is keyed by user account (for user tokens) or client ID (for client credentials).

Interactive vs. Silent Token Acquisition: Interactive prompts the user (via browser), silent tries to get a token from cache or uses a refresh token. If silent fails (e.g., token expired, user changed password), MSAL throws an exception that the app should handle by falling back to interactive.

Token Types

Access Token: A JSON Web Token (JWT) that contains claims about the client and user (if any). It is sent to the resource server in the Authorization header (Bearer scheme). Default lifetime is 60-90 minutes, but can be configured.

Refresh Token: Used to obtain a new access token without user interaction. Refresh tokens are opaque strings with longer lifetimes (up to 90 days of inactivity, then revoked).

ID Token: Used in OpenID Connect for authentication (not authorization). Contains user claims like name, email.

MSAL Token Acquisition Patterns

#### For Public Clients (Mobile/SPA):

// MSAL.NET for mobile app using Authorization Code + PKCE
IPublicClientApplication app = PublicClientApplicationBuilder
    .Create(clientId)
    .WithRedirectUri("msal{clientId}://auth")
    .Build();

AuthenticationResult result = await app.AcquireTokenInteractive(scopes)
    .WithPrompt(Prompt.SelectAccount)
    .ExecuteAsync();
string accessToken = result.AccessToken;

#### For Confidential Clients (Web App):

// MSAL.NET for web app using Authorization Code flow
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder
    .Create(clientId)
    .WithClientSecret(clientSecret)
    .WithRedirectUri(redirectUri)
    .Build();

AuthorizationCodeReceived = async (context) =>
{
    AuthenticationResult result = await app.AcquireTokenByAuthorizationCode(scopes, context.AuthorizationCode)
        .ExecuteAsync();
};

#### For Daemon Services:

// MSAL.NET using Client Credentials flow
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder
    .Create(clientId)
    .WithClientSecret(clientSecret)
    .Build();

AuthenticationResult result = await app.AcquireTokenForClient(scopes)
    .ExecuteAsync();

#### For On-Behalf-Of:

// Middle-tier API exchanging incoming token for downstream token
UserAssertion userAssertion = new UserAssertion(incomingToken);
AuthenticationResult result = await app.AcquireTokenOnBehalfOf(scopes, userAssertion)
    .ExecuteAsync();

Token Validation

When a resource server receives an access token, it must validate:

The token is not expired (check exp claim).

The token was issued by a trusted issuer (iss claim matches Azure AD tenant).

The audience (aud claim) matches the resource server's client ID.

The token has the required scopes (scp claim for delegated permissions, roles claim for app permissions).

The token signature is valid (using Azure AD's JWKS endpoint).

Error Handling

Common MSAL exceptions: - MsalUiRequiredException: The user needs to re-authenticate (e.g., password change, MFA required). The app should invoke interactive acquisition. - MsalServiceException: An error from Azure AD (e.g., invalid client secret, tenant not found). - MsalClientException: A client-side error (e.g., invalid redirect URI).

Best Practices for AZ-204

Always use the most secure flow appropriate for your client type: Authorization Code + PKCE for public clients, Client Credentials for daemons.

Store client secrets in Azure Key Vault, not in code or config files.

Use scopes with least privilege. Request only what you need.

Handle token expiration gracefully: MSAL caches and refreshes automatically, but you must catch MsalUiRequiredException to trigger interactive login.

For web apps, use the ASP.NET Core middleware (Microsoft.Identity.Web) which integrates MSAL seamlessly.

Walk-Through

1

Register the Application in Azure AD

First, register your client application in the Azure portal under Azure Active Directory > App registrations. Provide a name, select supported account types (e.g., single tenant, multi-tenant), and set a redirect URI (e.g., https://localhost:5001/signin-oidc for web apps, or a custom scheme like myapp://auth for mobile). The registration yields a Client ID (Application ID) which is a GUID. For confidential clients, generate a client secret (a string) or upload a certificate. For public clients, no secret is needed. Also configure API permissions: add Microsoft Graph permissions (e.g., User.Read) or custom scopes for your own API. This step is mandatory for any OAuth flow.

2

Initialize MSAL with Client ID and Authority

In your application code, create an MSAL application object. For a public client, use `PublicClientApplicationBuilder.Create(clientId).WithAuthority(authority).Build()`. The authority is the Azure AD endpoint, e.g., `https://login.microsoftonline.com/{tenantId}` or `https://login.microsoftonline.com/common` for multi-tenant. For confidential clients, use `ConfidentialClientApplicationBuilder` and add `.WithClientSecret(secret)` or `.WithCertificate(certificate)`. The authority determines which users can authenticate. Use `common` to allow any Microsoft account or Azure AD user, `organizations` for only Azure AD users, or a specific tenant ID for single-tenant apps.

3

Acquire Token Using Appropriate Flow

Call the appropriate `AcquireToken*` method. For public clients with user interaction, use `AcquireTokenInteractive(scopes)` which opens a browser window. For silent acquisition (e.g., after initial login), use `AcquireTokenSilent(scopes, account)`. For daemon apps, use `AcquireTokenForClient(scopes)`. For web apps handling the authorization code callback, use `AcquireTokenByAuthorizationCode(scopes, code)`. Each method returns an `AuthenticationResult` containing the access token, expiration time, and (for user flows) the account object. The scopes parameter is an array of strings like `["User.Read", "Mail.Read"]`. Always request the minimum scopes needed.

4

Use the Access Token to Call an API

Attach the access token to HTTP requests as a Bearer token in the Authorization header. Example: `request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);`. The resource server (e.g., Microsoft Graph) will validate the token. If the token is expired, the API returns HTTP 401 Unauthorized. Your app should then attempt to acquire a new token silently (using refresh token) or interactively. MSAL's token cache handles refresh automatically, but you must handle the `MsalUiRequiredException` if silent refresh fails.

5

Handle Token Expiry and Refresh

MSAL caches tokens and automatically attempts to refresh them when they expire. However, the refresh token itself may expire or be revoked. The `AcquireTokenSilent` method attempts to get a fresh token from cache or use the refresh token. If it fails (throws `MsalUiRequiredException`), you must prompt the user to re-authenticate interactively. Do not cache the access token yourself; rely on MSAL's cache. For daemon apps, the client credentials flow does not use refresh tokens—MSAL will re-acquire a token when the current one expires. The default access token lifetime is 60 minutes, but can be configured via Azure AD policies.

What This Looks Like on the Job

Enterprise Scenario 1: Multi-Tier Web API with On-Behalf-Of Flow

A large enterprise has a front-end web app (Angular SPA) that calls a middle-tier Web API (ASP.NET Core), which then calls Microsoft Graph to read user profiles. The SPA uses MSAL.js with the Authorization Code + PKCE flow to get an access token for the middle-tier API. The middle-tier API receives this token and uses the OBO flow (via MSAL.NET's AcquireTokenOnBehalfOf) to exchange it for a token to Microsoft Graph. This ensures the user's identity flows through the entire chain, and the middle-tier never stores user credentials. Performance considerations: token exchange adds latency (~100-200ms per call), so the middle-tier should cache the downstream token. Common misconfiguration: the middle-tier API must be registered in Azure AD with the oauth2AllowImplicitFlow set to false (it should use authorization code) and must have the required API permissions for Graph. If the downstream token expires, the middle-tier must handle MsalUiRequiredException gracefully—typically by returning 401 to the client, which then re-acquires a fresh token interactively.

Scenario 2: Daemon Service for Automated Reporting

A background service (Windows Service or Azure Function) needs to access Microsoft Graph to read all user mailboxes for compliance reporting. This is a server-to-server scenario with no user interaction. The service uses the Client Credentials flow. It is registered as a confidential client in Azure AD with a client secret stored in Azure Key Vault. The app registration has Application Permissions (e.g., Mail.Read.All) granted by an admin. The service calls AcquireTokenForClient with scopes like https://graph.microsoft.com/.default (which requests all app permissions). The token is cached and refreshed automatically by MSAL. Key considerations: the client secret must be rotated periodically; using a certificate instead of a secret is more secure and recommended for production. If the service runs at scale (thousands of requests per minute), ensure the token cache is thread-safe (MSAL handles this). Common mistake: using delegated permissions (User.Read) instead of app permissions (User.Read.All)—the former requires a user context and will fail.

Scenario 3: Mobile App with Social Login

A consumer-facing mobile app (Xamarin) allows users to sign in with their Microsoft account or Azure AD credentials. The app uses MSAL with the Authorization Code + PKCE flow. The redirect URI is set to a custom scheme (e.g., myapp://auth). The app requests scopes like User.Read and Mail.Send. MSAL opens the system browser for authentication. After the user authenticates, the app receives an access token and caches it. If the token expires, AcquireTokenSilent attempts to refresh. If the user changes their password or revokes consent, MsalUiRequiredException is thrown, and the app re-initiates interactive login. Common pitfalls: incorrect redirect URI (must match exactly, including case and trailing slashes), missing kSecAttrAccessGroup on iOS for keychain sharing, and not handling the broker (Microsoft Authenticator) on Android for single sign-on. The app should also handle the case where the user cancels the authentication.

How AZ-204 Actually Tests This

What AZ-204 Tests on OAuth 2.0 and MSAL

The exam objective 3.1 (Secure access to cloud resources) includes implementing OAuth 2.0 flows, using MSAL, and managing tokens. Specifically, you must know:

Which flow to use for different client types (web app, SPA, mobile, daemon).

How to register an application in Azure AD and configure permissions.

How to acquire tokens using MSAL in C# (especially AcquireTokenInteractive, AcquireTokenSilent, AcquireTokenForClient, AcquireTokenOnBehalfOf).

How to handle exceptions like MsalUiRequiredException.

The difference between delegated permissions (scopes) and application permissions (roles).

Token validation (issuer, audience, expiration, signature).

Common Wrong Answers and Why Candidates Choose Them

1.

Using Client Credentials flow for a mobile app: Candidates see that the app needs to call an API and think 'no user interaction' is the simplest. But mobile apps are public clients; client credentials require a client secret which cannot be stored securely on a device. The correct flow is Authorization Code + PKCE.

2.

Storing the access token in a cookie or local storage: Candidates think they need to persist the token for later use. But access tokens are bearer tokens—if stolen, an attacker can impersonate the user. The correct approach is to rely on MSAL's token cache, which encrypts tokens on the device.

3.

Using `AcquireTokenForClient` for a web API that calls another API on behalf of the user: This flow would get a token for the client itself, not for the user. The downstream API would see the client identity, not the user. The correct flow is On-Behalf-Of (AcquireTokenOnBehalfOf).

4.

Setting the access token lifetime to 24 hours to reduce prompts: Customizing token lifetimes is possible via Azure AD policies, but it reduces security. The exam expects you to know the default lifetime (60-90 minutes) and that refresh tokens handle long sessions. Changing lifetimes is not recommended.

Numbers and Terms That Appear Verbatim

Default access token lifetime: 60-90 minutes (configurable up to 24 hours).

Default refresh token lifetime: 90 days of inactivity (then revoked).

MSAL exception types: MsalUiRequiredException, MsalServiceException, MsalClientException.

Scopes for Microsoft Graph: https://graph.microsoft.com/.default (for app permissions), User.Read, Mail.Send.

Redirect URI formats: https://* for web apps, msal{clientId}://auth for mobile apps using broker.

Edge Cases the Exam Loves

Multi-tenant app: The authority must be common or organizations. The token's tid claim identifies the tenant.

Conditional Access: If the user needs MFA, AcquireTokenSilent will throw MsalUiRequiredException. The app must re-authenticate with WithPrompt(Prompt.ForceLogin) or WithClaims(claims).

Token caching in web apps: For a web app that serves multiple users, the token cache must be scoped per user (e.g., using session ID or distributed cache). MSAL's ASP.NET Core extension (Microsoft.Identity.Web) handles this automatically.

How to Eliminate Wrong Answers

If the scenario mentions 'no user interaction' or 'background service', the answer is Client Credentials flow.

If the scenario mentions 'mobile app' or 'SPA', the answer must include PKCE (look for 'code verifier' or 'S256').

If the scenario involves a middle-tier API calling another API, the answer is On-Behalf-Of flow.

If the question asks about handling token expiration, the answer should involve AcquireTokenSilent and handling MsalUiRequiredException.

If the question asks about storing secrets, the answer should involve Azure Key Vault or certificates.

Key Takeaways

OAuth 2.0 is an authorization framework; use OpenID Connect for authentication.

Authorization Code + PKCE is the recommended flow for public clients (SPAs, mobile).

Client Credentials flow is for daemon services with no user.

On-Behalf-Of flow chains tokens through a middle-tier API.

MSAL caches tokens automatically; handle MsalUiRequiredException for silent refresh failures.

Access token default lifetime: 60-90 minutes; refresh token: up to 90 days inactive.

Always request least-privilege scopes (e.g., User.Read instead of User.Read.All).

Store client secrets in Azure Key Vault, not in code or config.

For multi-tenant apps, use authority 'common' or 'organizations'.

Token validation must check issuer, audience, expiration, and signature.

Easy to Mix Up

These come up on the exam all the time. Here's how to tell them apart.

Authorization Code Flow

Used for web apps with a user context.

Requires a client secret (confidential client).

Returns an access token and a refresh token.

User authenticates interactively.

Scopes represent delegated permissions (user consent).

Client Credentials Flow

Used for server-to-server (daemon) scenarios.

Requires a client secret or certificate (confidential client).

Returns only an access token (no refresh token).

No user interaction; client authenticates itself.

Scopes represent application permissions (admin consent).

Authorization Code + PKCE

Recommended for SPAs and mobile apps.

Uses a code verifier and challenge to prevent interception.

Token is returned via a secure backchannel (code exchange).

Supports refresh tokens.

More secure against token leakage.

Implicit Flow (Deprecated)

Deprecated by OAuth 2.0 Security BCP.

Token is returned directly in the URL fragment.

No refresh tokens (SPA must re-authenticate).

Vulnerable to token interception and replay.

Not recommended for new applications.

Watch Out for These

Mistake

OAuth 2.0 is an authentication protocol.

Correct

OAuth 2.0 is an authorization framework, not authentication. It delegates access to resources. OpenID Connect (built on OAuth 2.0) provides authentication by adding an ID token.

Mistake

Access tokens are only valid for 60 minutes and cannot be refreshed.

Correct

Access tokens have a default lifetime of 60-90 minutes, but they can be refreshed using a refresh token. MSAL automatically refreshes tokens when they expire, as long as a valid refresh token exists.

Mistake

The Implicit flow is recommended for single-page apps.

Correct

The Implicit flow is deprecated. The Authorization Code flow with PKCE is now recommended for SPAs because it provides better security by preventing token interception.

Mistake

Client credentials flow can be used in mobile apps.

Correct

Client credentials flow requires a client secret, which cannot be securely stored on a mobile device. Mobile apps are public clients and must use Authorization Code + PKCE.

Mistake

MSAL caches tokens in memory by default, which is fine for all scenarios.

Correct

MSAL does cache tokens in memory, but for web apps serving multiple users, you must implement a distributed token cache (e.g., using SQL Server or Redis) to persist tokens across requests. Otherwise, each server instance will have its own cache, causing users to be prompted repeatedly.

Do You Actually Know This?

Reveal each answer, then mark whether you got it right. Score 60%+ to unlock the next chapter.

Frequently Asked Questions

What is the difference between OAuth 2.0 and OpenID Connect?

OAuth 2.0 is for authorization—granting access to resources. OpenID Connect (OIDC) is an authentication layer built on OAuth 2.0 that adds an ID token (a JWT containing user identity claims). In practice, Azure AD uses OIDC for sign-in and OAuth 2.0 for API access. On the exam, know that OIDC provides `id_token` while OAuth 2.0 provides `access_token`.

How do I choose the right MSAL flow for my application?

If your app has a user interface (web app, SPA, mobile), use Authorization Code + PKCE. If it's a daemon or background service with no user, use Client Credentials. If you have a middle-tier API that needs to call another API on behalf of the user, use On-Behalf-Of. For web apps that can securely store a secret, Authorization Code flow (without PKCE) is also acceptable, but PKCE is recommended even for confidential clients.

What should I do when AcquireTokenSilent throws MsalUiRequiredException?

Catch the exception and fall back to an interactive token acquisition (e.g., AcquireTokenInteractive). This exception indicates that the user needs to re-authenticate—for example, because their password changed, they need to consent to new scopes, or MFA is required. Do not ignore it; always handle it to provide a seamless user experience.

How do I implement token caching in a web app with multiple users?

Use a distributed token cache that is keyed by user identifier (e.g., account ID or session ID). The Microsoft.Identity.Web library provides `AddMicrosoftIdentityWebApp` which automatically uses an in-memory cache per user session. For production, configure a distributed cache like SQL Server or Redis using `AddDistributedTokenCache`. This ensures tokens persist across server restarts and scale-out scenarios.

What are the differences between delegated permissions and application permissions?

Delegated permissions (scopes) are used in user-facing flows (Authorization Code, OBO). They represent the user's consent for the app to act on their behalf. Application permissions (roles) are used in Client Credentials flow; they represent the app's own permissions without a user context. For example, `User.Read` (delegated) allows reading the signed-in user's profile, while `User.Read.All` (application) allows reading all users' profiles. Admin consent is required for application permissions.

Can I use the same access token for multiple APIs?

No. An access token is scoped to a specific audience (the `aud` claim). For example, a token for Microsoft Graph cannot be used to call your custom API. You must request separate tokens for each resource by specifying their respective scopes. MSAL can cache multiple tokens per user per resource.

How do I validate an access token in my custom API?

Use a JWT validation library (e.g., Microsoft.IdentityModel.Tokens). Configure the validation parameters: ValidIssuer (your Azure AD tenant ID), ValidAudience (your API's client ID), IssuerSigningKeys (from the JWKS endpoint at https://login.microsoftonline.com/{tenant}/discovery/v2.0/keys). Also check the `scp` or `roles` claim for required permissions. The middleware can do this automatically (e.g., `AddMicrosoftIdentityWebApi` in ASP.NET Core).

Terms Worth Knowing

Ready to put this to the test?

You've just covered OAuth 2.0 Flows and MSAL Library — now see how well it sticks with free AZ-204 practice questions. Full explanations included, no account needed.

Done with this chapter?