Skip to content

Commit

Permalink
Merge pull request #428 from MicrosoftDocs/content-health
Browse files Browse the repository at this point in the history
Content health: Update Acrolinx scores
  • Loading branch information
localden authored Jun 10, 2024
2 parents d55f1fd + 3540936 commit 8a67b6f
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 50 deletions.
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
---
title: Client assertions (MSAL.NET)
description: Learn about signed client assertions support for confidential client applications in the Microsoft Authentication Library for .NET (MSAL.NET).
services: active-directory
author: Dickson-Mwendia
manager: CelesteDG

ms.service: msal
ms.subservice: msal-dotnet
ms.topic: conceptual
ms.workload: identity
ms.date: 03/29/2023
ms.date: 06/04/2024
ms.author: dmwendia
ms.reviewer: saeeda, jmprieur
ms.custom: devx-track-csharp, aaddev, devx-track-dotnet
Expand All @@ -35,9 +34,9 @@ MSAL.NET has four methods to provide either credentials or assertions to the con
### Client assertions

This is useful if you want to handle the certificate yourself. For example, if you wish to use Azure KeyVault's APIs for signing, which eliminates the need for downloading the certificates. A signed client assertion takes the form of a signed JWT with the payload containing the required authentication claims mandated by Microsoft Entra ID, Base64 encoded. Or it can be a JWT form a different Identity Provider, for the "Federated Identity Credential" scenario.
This is useful if you want to handle the certificate yourself. For example, if you wish to use Azure KeyVault's APIs for signing, which eliminates the need for downloading the certificates. A signed client assertion takes the form of a signed JWT with the payload containing the required authentication claims mandated by Microsoft Entra ID, Base64 encoded. Or it can be a JWT from a different Identity Provider, for the "Federated Identity Credential" scenario.

Use the delegate, which enables you to compute the assertion everytime MSAL needs to get a new token from the Identity Provider. Note that MSAL will not invoke your delegate if a token is found in the cache.
Use the delegate, which enables you to compute the assertion every time MSAL needs to get a new token from the identity provider. MSAL doesn't invoke your delegate if a token is found in the cache.

```csharp
string signedClientAssertion = GetOrComputeAssertion();
Expand All @@ -53,11 +52,11 @@ The [claims expected by Microsoft Entra ID](/azure/active-directory/develop/cert

Claim type | Value | Description
---------- | ---------- | ----------
aud | `https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token` | The "aud" (audience) claim identifies the recipients that the JWT is intended for (here Microsoft Entra ID) See [RFC 7519, Section 4.1.3](https://tools.ietf.org/html/rfc7519#section-4.1.3). In this case, that recipient is the token endpoint of the identity provider
exp | 1601519414 | The "exp" (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing. See [RFC 7519, Section 4.1.4](https://tools.ietf.org/html/rfc7519#section-4.1.4). This allows the assertion to be used until then, so keep it short - 5-10 minutes after `nbf` at most. Microsoft Entra ID does not place restrictions on the `exp` time currently.
iss | {ClientID} | The "iss" (issuer) claim identifies the principal that issued the JWT, in this case your client application. Use the GUID application ID.
jti | (a Guid) | The "jti" (JWT ID) claim provides a unique identifier for the JWT. The identifier value MUST be assigned in a manner that ensures that there is a negligible probability that the same value will be accidentally assigned to a different data object; if the application uses multiple issuers, collisions MUST be prevented among values produced by different issuers as well. The "jti" value is a case-sensitive string. [RFC 7519, Section 4.1.7](https://tools.ietf.org/html/rfc7519#section-4.1.7)
nbf | 1601519114 | The "nbf" (not before) claim identifies the time before which the JWT MUST NOT be accepted for processing. [RFC 7519, Section 4.1.5](https://tools.ietf.org/html/rfc7519#section-4.1.5). Using the current time is appropriate.
aud | `https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token` | The "aud" (audience) claim identifies the recipients that the JWT is intended for (here Microsoft Entra ID) See [RFC 7519, Section 4.1.3](https://tools.ietf.org/html/rfc7519#section-4.1.3). In this case, that recipient is the token endpoint of the identity provider
exp | 1601519414 | The "exp" (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing. See [RFC 7519, Section 4.1.4](https://tools.ietf.org/html/rfc7519#section-4.1.4). This allows the assertion to be used until then, so keep it short - 5-10 minutes after `nbf` at most. Microsoft Entra ID doesn't place restrictions on the `exp` time currently.
iss | {ClientID} | The "iss" (issuer) claim identifies the principal that issued the JWT, in this case your client application. Use the GUID application ID.
jti | (a Guid) | The "jti" (JWT ID) claim provides a unique identifier for the JWT. The identifier value MUST be assigned in a manner that ensures that there's a negligible probability that the same value can be accidentally assigned to a different data object. If the application uses multiple issuers, collisions MUST be prevented among values produced by different issuers as well. The "jti" value is a case-sensitive string. [RFC 7519, Section 4.1.7](https://tools.ietf.org/html/rfc7519#section-4.1.7)
nbf | 1601519114 | The "nbf" (not before) claim identifies the time before which the JWT MUST NOT be accepted for processing. [RFC 7519, Section 4.1.5](https://tools.ietf.org/html/rfc7519#section-4.1.5). Using the current time is appropriate.
sub | {ClientID} | The "sub" (subject) claim identifies the subject of the JWT, in this case also your application. Use the same value as `iss`.

If you use a certificate as a client secret, the certificate must be deployed safely. We recommend that you store the certificate in a secure spot supported by the platform, such as in the certificate store on Windows or by using Azure Key Vault.
Expand Down Expand Up @@ -89,7 +88,7 @@ This is an example using [Microsoft.IdentityModel.JsonWebTokens](https://www.nug
}
```

Alternatively, if you do not wish to use Microsoft.IdentityModel.JsonWebTokens:
Alternatively, if you don't wish to use Microsoft.IdentityModel.JsonWebTokens:

```csharp
static string Base64UrlEncode(byte[] arg)
Expand Down Expand Up @@ -139,7 +138,7 @@ static string GetSignedClientAssertion(X509Certificate2 certificate, string tena

In some cases, developers want to inject some claims into the assertions, but would still like MSAL to handle the creation of the assertion and the signing.

`WithClientClaims(X509Certificate2 certificate, IDictionary<string, string> claimsToSign, bool mergeWithDefaultClaims = true)` will produce a signed assertion containing the claims expected by Microsoft Entra ID plus additional client claims that you want to send.
`WithClientClaims(X509Certificate2 certificate, IDictionary<string, string> claimsToSign, bool mergeWithDefaultClaims = true)` produces a signed assertion containing the claims expected by Microsoft Entra ID plus additional client claims that you want to send.

```csharp
string ipAddress = "192.168.1.2";
Expand All @@ -152,6 +151,6 @@ app = ConfidentialClientApplicationBuilder.Create(config.ClientId)

```

If one of the claims in the dictionary that you pass in is the same as one of the mandatory claims, the additional claim's value will be taken into account. It will override the claims computed by MSAL.NET.
If one of the claims in the dictionary that you pass in is the same as one of the mandatory claims, the additional claim's value is taken into account. It overrides the claims computed by MSAL.NET.

If you want to provide your own claims, including the mandatory claims expected by Microsoft Entra ID, pass in `false` for the `mergeWithDefaultClaims` parameter.
20 changes: 10 additions & 10 deletions msal-dotnet-articles/advanced/exceptions/msal-error-handling.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
---
title: Handle errors and exceptions in MSAL.NET
description: Learn how to handle errors and exceptions, Conditional Access claims challenges, and retries in MSAL.NET.
services: active-directory
author: Dickson-Mwendia
manager: CelesteDG

ms.service: msal
ms.subservice: msal-dotnet
ms.topic: conceptual
ms.workload: identity
ms.date: 01/25/2023
ms.date: 06/04/2024
ms.author: dmwendia
ms.reviewer: saeeda, jmprieur
ms.custom: aaddev, devx-track-dotnet
Expand All @@ -26,10 +25,11 @@ ms.custom: aaddev, devx-track-dotnet

[MsalServiceException](/dotnet/api/microsoft.identity.client.msalserviceexception) is thrown when the Identity Provider (Microsoft Entra ID) returns an error. It's a translation of the server error.

[MsalUIRequiredException](/dotnet/api/microsoft.identity.client.msaluirequiredexception) is type of [MsalServiceException](/dotnet/api/microsoft.identity.client.msalserviceexception) and indicates that user interaction is required, for example because MFA is required or because the user has changed their password and a token can't be acquired silently.
[MsalUIRequiredException](/dotnet/api/microsoft.identity.client.msaluirequiredexception) is type of [MsalServiceException](/dotnet/api/microsoft.identity.client.msalserviceexception) and indicates that user interaction is required. For example, when multifactor authentication (MFA) is required or when the user changes their password and a token can't be acquired silently.


### Processing exceptions

When processing .NET exceptions, you can use the exception type itself and the `ErrorCode` member to distinguish between exceptions. `ErrorCode` values are constants of type [MsalError](/dotnet/api/microsoft.identity.client.msalerror).

You can also have a look at the fields of [MsalClientException](/dotnet/api/microsoft.identity.client.msalexception), [MsalServiceException](/dotnet/api/microsoft.identity.client.msalserviceexception), and [MsalUIRequiredException](/dotnet/api/microsoft.identity.client.msaluirequiredexception).
Expand All @@ -45,23 +45,23 @@ Here are the common exceptions that might be thrown and some possible mitigation
| Exception | Error code | Mitigation|
| --- | --- | --- |
| [MsalUiRequiredException](/dotnet/api/microsoft.identity.client.msaluirequiredexception) | AADSTS65001: The user or administrator hasn't consented to use the application with ID '{appId}' named '{appName}'. Send an interactive authorization request for this user and resource.| Get user consent first. If you aren't using .NET Core (which doesn't have any Web UI), call (once only) `AcquireTokenInteractive`. If you're using .NET core or don't want to do an `AcquireTokenInteractive`, the user can navigate to a URL to give consent: `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={clientId}&response_type=code&scope=user.read`. to call `AcquireTokenInteractive`: `app.AcquireTokenInteractive(scopes).WithAccount(account).WithClaims(ex.Claims).ExecuteAsync();`|
| [MsalUiRequiredException](/dotnet/api/microsoft.identity.client.msaluirequiredexception) | AADSTS50079: The user is required to use [multi-factor authentication (MFA)](/azure/active-directory/authentication/concept-mfa-howitworks).| There's no mitigation. If MFA is configured for your tenant and Microsoft Entra ID decides to enforce it, fall back to an interactive flow such as `AcquireTokenInteractive`.|
| [MsalUiRequiredException](/dotnet/api/microsoft.identity.client.msaluirequiredexception) | AADSTS50079: The user is required to use [multifactor authentication (MFA)](/azure/active-directory/authentication/concept-mfa-howitworks).| There's no mitigation. If MFA is configured for your tenant and Microsoft Entra ID decides to enforce it, fall back to an interactive flow such as `AcquireTokenInteractive`.|
| [MsalServiceException](/dotnet/api/microsoft.identity.client.msalserviceexception) |AADSTS90010: The grant type isn't supported over the */common* or */consumers* endpoints. Use the */organizations* or tenant-specific endpoint. You used */common*.| As explained in the message from Microsoft Entra ID, the authority needs to have a tenant or otherwise */organizations*.|
| [MsalServiceException](/dotnet/api/microsoft.identity.client.msalserviceexception) | AADSTS70002: The request body must contain the following parameter: `client_secret or client_assertion`.| This exception can be thrown if your application wasn't registered as a public client application in Microsoft Entra ID. In the Azure portal, edit the manifest for your application and set `allowPublicClient` to `true`. |
| [MsalServiceException](/dotnet/api/microsoft.identity.client.msalserviceexception) | AADSTS70002: The request body must contain the following parameter: `client_secret or client_assertion`.| This exception can be thrown if your application wasn't registered as a public client application in Microsoft Entra ID. In the Microsoft Entra admin center, edit the manifest for your application and set `allowPublicClient` to `true`. |
| [MsalClientException](/dotnet/api/microsoft.identity.client.msalclientexception)| `unknown_user Message`: Couldn't identify logged in user| The library was unable to query the current Windows logged-in user or this user isn't Active Directory or Microsoft Entra joined (work-place joined users aren't supported). Mitigation: Implement your own logic to fetch the username (for example, [email protected]) and use the `AcquireTokenByIntegratedWindowsAuth` form that takes in the username.|
| [MsalClientException](/dotnet/api/microsoft.identity.client.msalclientexception)|integrated_windows_auth_not_supported_managed_user| This method relies on a protocol exposed by Active Directory (AD). If a user was created in Microsoft Entra ID without AD backing ("managed" user), this method will fail. Users created in AD and backed by Microsoft Entra ID ("federated" users) can benefit from this non-interactive method of authentication. Mitigation: Use interactive authentication.|
| [MsalClientException](/dotnet/api/microsoft.identity.client.msalclientexception)|integrated_windows_auth_not_supported_managed_user| This method relies on a protocol exposed by Active Directory (AD). If a user was created in Microsoft Entra ID without AD backing ("managed" user), this method fails. Users created in AD and backed by Microsoft Entra ID ("federated" users) can benefit from this non-interactive method of authentication. Mitigation: Use interactive authentication.|

### `MsalUiRequiredException`

One of common status codes returned from MSAL.NET when calling `AcquireTokenSilent()` is `MsalError.InvalidGrantError`. This status code means that the application should call the authentication library again, but in interactive mode (AcquireTokenInteractive or AcquireTokenByDeviceCodeFlow for public client applications, do have a challenge in Web apps). This is because additional user interaction is required before authentication token can be issued.

Most of the time when `AcquireTokenSilent` fails, it is because the token cache doesn't have tokens matching your request. Access tokens expire in 1 hour, and `AcquireTokenSilent` will try to fetch a new one based on a refresh token (in OAuth2 terms, this is the "Refresh Token' flow). This flow can also fail for various reasons, for example if a tenant admin configures more stringent login policies.
Most of the time when `AcquireTokenSilent` fails, it is because the token cache doesn't have tokens matching your request. Access tokens expire in 1 hour, and `AcquireTokenSilent` tries to fetch a new one based on a refresh token (in OAuth2 terms, this is the "Refresh Token' flow). This flow can also fail for various reasons, for example if a tenant admin configures more stringent sign-in policies.

The interaction aims at having the user do an action. Some of those conditions are easy for users to resolve (for example, accept Terms of Use with a single click), and some can't be resolved with the current configuration (for example, the machine in question needs to connect to a specific corporate network). Some help the user setting-up multi-factor authentication, or install Microsoft Authenticator on their device.
The interaction aims at having the user do an action. Some of those conditions are easy for users to resolve (for example, accept Terms of Use with a single click), and some can't be resolved with the current configuration (for example, the machine in question needs to connect to a specific corporate network). Some help the user setting-up multifactor authentication, or install Microsoft Authenticator on their device.

### `MsalUiRequiredException` classification enumeration

MSAL exposes a `Classification` field, which you can read to provide a better user experience. For example to tell the user that their password expired or that they'll need to provide consent to use some resources. The supported values are part of the [`UiRequiredExceptionClassification`](/dotnet/api/microsoft.identity.client.uirequiredexceptionclassification) enum:
MSAL exposes a `Classification` field, which you can read to provide a better user experience. For example, to tell the user that their password expired or that they need to provide consent to use some resources. The supported values are part of the [`UiRequiredExceptionClassification`](/dotnet/api/microsoft.identity.client.uirequiredexceptionclassification) enum:

| Classification | Meaning | Recommended handling |
|-------------------|-------------------|----------------------|
Expand Down Expand Up @@ -133,7 +133,7 @@ catch (MsalUiRequiredException ex) when (ex.ErrorCode == MsalError.InvalidGrantE
```
[!INCLUDE [Active directory error handling claims challenges](../../includes/error-handling-claims-challenges.md)]

When calling an API requiring Conditional Access from MSAL.NET, your application will need to handle claim challenge exceptions. This will appear as an [MsalServiceException](/dotnet/api/microsoft.identity.client.msalserviceexception) where the [Claims](/dotnet/api/microsoft.identity.client.msalserviceexception.claims) property won't be empty.
When calling an API requiring Conditional Access from MSAL.NET, your application needs to handle claim challenge exceptions. This appears as an [MsalServiceException](/dotnet/api/microsoft.identity.client.msalserviceexception) where the [Claims](/dotnet/api/microsoft.identity.client.msalserviceexception.claims) property won't be empty.

To handle the claim challenge, use <xref:Microsoft.Identity.Client.AbstractAcquireTokenParameterBuilder%601.WithClaims(System.String)>.

Expand Down
Loading

0 comments on commit 8a67b6f

Please sign in to comment.