From 3bf8f436bf3de3f4271de99b48cf530cbdc69262 Mon Sep 17 00:00:00 2001 From: Bogdan Gavril Date: Sun, 22 Aug 2021 17:09:34 +0100 Subject: [PATCH] Add test infra and test for #1390 (#1400) --- src/Microsoft.Identity.Web/ClientInfo.cs | 1 + .../Microsoft.Identity.Web.xml | 2744 ++++++++++++++++- .../TokenCacheExtensions.cs | 29 +- .../{ => Util}/Base64UrlHelpers.cs | 3 +- .../Microsoft.Identity.Web.Test.Common.csproj | 1 + .../Mocks/MockHttpClientFactory.cs | 59 + .../Mocks/MockHttpCreator.cs | 60 + .../Mocks/MockHttpMessageHandler.cs | 57 + .../TestConstants.cs | 37 + .../Base64UrlHelpersTests.cs | 1 + .../CacheExtensionsTests.cs | 78 + .../ClientInfoTests.cs | 1 + .../WebAppExtensionsTests.cs | 1 + 13 files changed, 2939 insertions(+), 133 deletions(-) rename src/Microsoft.Identity.Web/{ => Util}/Base64UrlHelpers.cs (97%) create mode 100644 tests/Microsoft.Identity.Web.Test.Common/Mocks/MockHttpClientFactory.cs create mode 100644 tests/Microsoft.Identity.Web.Test.Common/Mocks/MockHttpCreator.cs create mode 100644 tests/Microsoft.Identity.Web.Test.Common/Mocks/MockHttpMessageHandler.cs diff --git a/src/Microsoft.Identity.Web/ClientInfo.cs b/src/Microsoft.Identity.Web/ClientInfo.cs index e64d05c8c..fd9d0e210 100644 --- a/src/Microsoft.Identity.Web/ClientInfo.cs +++ b/src/Microsoft.Identity.Web/ClientInfo.cs @@ -4,6 +4,7 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; +using Microsoft.Identity.Web.Util; namespace Microsoft.Identity.Web { diff --git a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml index b4fa37ae1..2a184d984 100644 --- a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml +++ b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml @@ -4,6 +4,235 @@ Microsoft.Identity.Web + + + Extension methods for . + + + + + Creates the from the values found + in an . + + The instance. + A built from . + + + + Extension methods related to App Services authentication (Easy Auth). + + + + + Add authentication with App Services. + + Authentication builder. + The builder, to chain commands. + + + + Default values related to AppServiceAuthentication handler. + + + + + The default value used for AppServiceAuthenticationOptions.AuthenticationScheme. + + + + + App service authentication handler. + + + + + Constructor for the AppServiceAuthenticationHandler. + Note the parameters are required by the base class. + + App service authentication options. + Logger factory. + URL encoder. + System clock. + + + + + + + Information about the App Services configuration on the host. + + + + + Is App Services authentication enabled?. + + + + + Logout URL for App Services Auth web sites. + + + + + ClientID of the App Services Auth web site. + + + + + Client secret of the App Services Auth web site. + + + + + Issuer of the App Services Auth web site. + + + + + Get headers from environment to help debugging App Services authentication. + + + + + Get the ID token from the headers sent by App services authentication. + + Headers. + The ID Token. + + + + Get the IDP from the headers sent by App services authentication. + + Headers. + The IDP. + + + + Get the user claims from the headers and environment variables. + + Headers. + User claims. + + + + Options for Azure App Services authentication. + + + + + Implementation of ITokenAcquisition for App Services authentication (EasyAuth). + + + + + Please call GetOrCreateApplication instead of accessing this field directly. + + + + + Constructor of the AppServicesAuthenticationTokenAcquisition. + + The App token cache provider. + Access to the HttpContext of the request. + HTTP client factory. + + + + + + + + + + + + + + + + + + + + + + + + + Filter used on a controller action to trigger incremental consent. + + + The following controller action will trigger. + + [AuthorizeForScopes(Scopes = new[] {"Mail.Send"})] + public async Task<IActionResult> SendEmail() + { + } + + + + + + Scopes to request. + + + + + Key section on the configuration file that holds the scope value. + + + + + Azure AD B2C user flow. + + + + + Allows specifying an AuthenticationScheme if OpenIdConnect is not the default challenge scheme. + + + + + Handles the . + + Context provided by ASP.NET Core. + + + + Finds an MsalUiRequiredException in one of the inner exceptions. + + Exception from which we look for an MsalUiRequiredException. + The MsalUiRequiredException if there is one, null, otherwise. + + + + Extensions for . + + + + + Enables an Azure Function to act as/expose a protected web API, enabling bearer token authentication. Calling this method from your Azure function validates the token and exposes the identity of the user or app on behalf of which your function is called, in the HttpContext.User member, where your function can make use of it. + + The current HTTP Context, such as req.HttpContext. + A task indicating success or failure. In case of failure . + + + + Azure SDK token credential based on the ITokenAcquisition service. + + + + + Constructor from an ITokenAcquisition service. + + Token acquisition. + + + + + + + Description of a certificate. @@ -261,6 +490,117 @@ Description of the certificate. + + + Extensions for . + + + + + Gets the account identifier for an MSAL.NET account from a . + + Claims principal. + A string corresponding to an account identifier as defined in . + + + + Gets the unique object ID associated with the . + + The from which to retrieve the unique object ID. + This method returns the object ID both in case the developer has enabled or not claims mapping. + Unique object ID of the identity, or null if it cannot be found. + + + + Gets the Tenant ID associated with the . + + The from which to retrieve the tenant ID. + Tenant ID of the identity, or null if it cannot be found. + This method returns the tenant ID both in case the developer has enabled or not claims mapping. + + + + Gets the login-hint associated with a . + + Identity for which to complete the login-hint. + The login hint for the identity, or null if it cannot be found. + + + + Gets the domain-hint associated with an identity. + + Identity for which to compute the domain-hint. + The domain hint for the identity, or null if it cannot be found. + + + + Get the display name for the signed-in user, from the . + + Claims about the user/account. + A string containing the display name for the user, as determined by Azure AD (v1.0) and Microsoft identity platform (v2.0) tokens, + or null if the claims cannot be found. + See https://docs.microsoft.com/azure/active-directory/develop/id-tokens#payload-claims. + + + + Gets the user flow ID associated with the . + + The from which to retrieve the user flow ID. + User flow ID of the identity, or null if it cannot be found. + + + + Gets the Home Object ID associated with the . + + The from which to retrieve the sub claim. + Home Object ID (sub) of the identity, or null if it cannot be found. + + + + Gets the Home Tenant ID associated with the . + + The from which to retrieve the sub claim. + Home Tenant ID (sub) of the identity, or null if it cannot be found. + + + + Gets the NameIdentifierId associated with the . + + The from which to retrieve the NameIdentifierId claim. + Name identifier ID of the identity, or null if it cannot be found. + + + + Factory class to create objects. + + + + + Instantiate a from an account object ID and tenant ID. This can + be useful when the web app subscribes to another service on behalf of the user + and then is called back by a notification where the user is identified by their tenant + ID and object ID (like in Microsoft Graph Web Hooks). + + Tenant ID of the account. + Object ID of the account in this tenant ID. + A containing these two claims. + + + + private async Task GetChangedMessagesAsync(IEnumerable<Notification> notifications) + { + HttpContext.User = ClaimsPrincipalExtension.FromTenantIdAndObjectId(subscription.TenantId, + subscription.UserId); + foreach (var notification in notifications) + { + SubscriptionStore subscription = + subscriptionStore.GetSubscriptionInfo(notification.SubscriptionId); + string accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(scopes); + ...} + } + + + Constants for claim types. @@ -271,154 +611,1989 @@ Name claim: "name". - + + + Old Object Id claim: http://schemas.microsoft.com/identity/claims/objectidentifier. + + + + + New Object id claim: "oid". + + + + + PreferredUserName: "preferred_username". + + + + + Old TenantId claim: "http://schemas.microsoft.com/identity/claims/tenantid". + + + + + New Tenant Id claim: "tid". + + + + + ClientInfo claim: "client_info". + + + + + UniqueObjectIdentifier: "uid". + Home Object Id. + + + + + UniqueTenantIdentifier: "utid". + Home Tenant Id. + + + + + Older scope claim: "http://schemas.microsoft.com/identity/claims/scope". + + + + + Newer scope claim: "scp". + + + + + New Roles claim = "roles". + + + + + Old Role claim: "http://schemas.microsoft.com/ws/2008/06/identity/claims/role". + + + + + Subject claim: "sub". + + + + + Acr claim: "acr". + + + + + UserFlow claim: "http://schemas.microsoft.com/claims/authnclassreference". + + + + + Tfp claim: "tfp". + + + + + Name Identifier ID claim: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier". + + + + + General constants for Microsoft Identity Web. + + + + + LoginHint. + Represents the preferred_username claim in the ID token. + + + + + DomainHint. + Determined by the tenant Id. + + + + + Claims. + Determined from the signed-in user. + + + + + Bearer. + Predominant type of access token used with OAuth 2.0. + + + + + AzureAd. + Configuration section name for AzureAd. + + + + + AzureAdB2C. + Configuration section name for AzureAdB2C. + + + + + Scope. + + + + + Policy for B2C user flows. + The name of the policy to check against a specific user flow. + + + + + Constants related to the error messages. + + + + + Constants related to the log messages. + + + + + Extension class containing cookie policies (work around for same site). + + + + + Handles SameSite cookie issue according to the https://docs.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-3.1. + The default list of user agents that disallow "SameSite=None", + was taken from https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/. + + to update. + to chain. + + + + Handles SameSite cookie issue according to the docs: https://docs.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-3.1 + The default list of user agents that disallow "SameSite=None", was taken from https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/. + + to update. + If you don't want to use the default user agent list implementation, + the method sent in this parameter will be run against the user agent and if returned true, SameSite value will be set to Unspecified. + The default user agent list used can be found at: https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/. + to chain. + + + + Checks if the specified user agent supports "SameSite=None" cookies. + + Browser user agent. + + Incompatible user agents include: + + Versions of Chrome from Chrome 51 to Chrome 66 (inclusive on both ends). + Versions of UC Browser on Android prior to version 12.13.2. + Versions of Safari and embedded browsers on MacOS 10.14 and all browsers on iOS 12. + + Reference: https://www.chromium.org/updates/same-site/incompatible-clients. + + True, if the user agent does not allow "SameSite=None" cookie; otherwise, false. + + + + Implementation for the downstream web API. + + + + + Constructor. + + Token acquisition service. + Named options provider. + HTTP client. + Configuration options. + + + + + + + + + + + + + Merge the options from configuration and override from caller. + + Named configuration. + Delegate to override the configuration. + + + + Extension methods to support downstream web API services. + + + + + Adds a named downstream web API service related to a specific configuration section. + + Builder. + Name of the configuration for the service. + This is the name used when calling the service from controller/pages. + Configuration. + The builder for chaining. + + + + Adds a named downstream web API service initialized with delegates. + + Builder. + Name of the configuration for the service. + This is the name which will be used when calling the service from controller/pages. + Action to configure the options. + The builder for chaining. + + + + Extensions for the downstream web API. + + + + + Get a strongly typed response from the web API. + + Output type. + The downstream web API. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Path to the API endpoint relative to the base URL specified in the configuration. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + A strongly typed response from the web API. + + + + Calls the web API with an HttpPost, providing strongly typed input and getting + strongly typed output. + + Output type. + Input type. + The downstream web API. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Path to the API endpoint relative to the base URL specified in the configuration. + Input data sent to the API. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + A strongly typed response from the web API. + + + + Calls the web API endpoint with an HttpPut, providing strongly typed input data. + + Input type. + The downstream web API. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Path to the API endpoint relative to the base URL specified in the configuration. + Input data sent to the API. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + The value returned by the downstream web API. + + + + Calls the web API endpoint with an HttpPut, provinding strongly typed input data + and getting back strongly typed data. + + Output type. + Input type. + The downstream web API. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Path to the API endpoint relative to the base URL specified in the configuration. + Input data sent to the API. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + A strongly typed response from the web API. + + + + Call a web API endpoint with an HttpGet, + and return strongly typed data. + + Output type. + The downstream web API. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + The value returned by the downstream web API. + + + + Call a web API with a strongly typed input, with an HttpGet. + + Input type. + The downstream web API. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Input data. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + The value returned by the downstream web API. + + + + Options passed-in to call downstream web APIs. To call Microsoft Graph, see rather + MicrosoftGraphOptions in the Microsoft.Identity.Web.MicrosoftGraph assembly. + + + + + Base URL for the called downstream web API. For instance "https://graph.microsoft.com/beta/".. + + + + + Path relative to the (for instance "me"). + + + + + HTTP method used to call this downstream web API (by default Get). + + + + + Clone the options (to be able to override them). + + A clone of the options. + + + + Return the downstream web API URL. + + URL of the downstream web API. + + + + Clone the options (to be able to override them). + + A clone of the options. + + + + Interface used to call a downstream web API, for instance from controllers. + + + + + Calls the downstream web API for the user, based on a description of the + downstream web API in the configuration. + + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful on platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + HTTP context in the case where is + , , . + An that the application will process. + + + + Calls the downstream web API for the user, based on a description of the + downstream web API in the configuration. + + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful on platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + HTTP context in the case where is + , , . + An that the application will process. + + + + Calls a downstream web API consuming JSON with some data and returns data. + + Input type. + Output type. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Input parameter to the downstream web API. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + The value returned by the downstream web API. + + A list method that returns an IEnumerable<MyItem>>. + + public Task<IEnumerable<MyItem>> GetAsync() + { + return _downstreamWebApi.CallWebApiForUserAsync<object, IEnumerable<MyItem>>( + ServiceName, + null, + options => + { + options.RelativePath = $"api/todolist"; + }); + } + + + Example of editing. + + public Task<MyItem> EditAsync(MyItem myItem) + { + return _downstreamWebApi.CallWebApiForUserAsync<MyItem, MyItem>( + ServiceName, + nyItem, + options => + { + options.HttpMethod = HttpMethod.Patch; + options.RelativePath = $"api/todolist/{myItem.Id}"; + }); + } + + + + + + Calls a downstream web API consuming JSON with some data and returns data. + + Input type. + Output type. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Input parameter to the downstream web API. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + The value returned by the downstream web API. + + A list method that returns an IEnumerable<MyItem>>. + + public Task<IEnumerable<MyItem>> GetAsync() + { + return _downstreamWebApi.CallWebApiForUserAsync<object, IEnumerable<MyItem>>( + ServiceName, + null, + options => + { + options.RelativePath = $"api/todolist"; + }); + } + + + Example of editing. + + public Task<MyItem> EditAsync(MyItem myItem) + { + return _downstreamWebApi.CallWebApiForUserAsync<MyItem, MyItem>( + ServiceName, + nyItem, + options => + { + options.HttpMethod = HttpMethod.Patch; + options.RelativePath = $"api/todolist/{myItem.Id}"; + }); + } + + + + + + Calls the downstream web API for the app, with the required scopes. + + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Overrides the options proposed in the configuration described + by . + HTTP content in the case where is + , , . + An that the application will process. + + + + Calls the downstream web API for the app, with the required scopes. + + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + Overrides the options proposed in the configuration described + by . + HTTP content in the case where is + , , . + An that the application will process. + + + + A DelegatingHandler implementation that add an authorization header with a token for the application. + + + + + Initializes a new instance of the class. + + Token acquisition service. + Named options provider. + Name of the service describing the downstream web API. + + + + + + + Base class for Microsoft Identity authentication message handlers. + + + + + Gets the token acquisition service. + + + + + Initializes a new instance of the class. + + Token acquisition service. + Named options provider. + Name of the service describing the downstream web API. + + + + Gets the options for the specified request. + + The request. + The configured options. + + + + Base options passed-in to authenticate with Microsoft Identity. + + + + + Space separated scopes required to call the downstream web API. + For instance "user.read mail.read". + + + + + [Optional] tenant ID. This is used for specific scenarios where + the application needs to call a downstream web API on behalf of a user in several tenants. + It would mostly be used from code, not from the configuration. + + + + + [Optional]. User flow (in the case of a B2C downstream web API). If not + specified, the B2C downstream web API will be called with the default user flow from + . + + + + + Modifies the token acquisition request so that the acquired token is a Proof of Possession token (PoP), + rather than a Bearer token. + PoP tokens are similar to Bearer tokens, but are bound to the HTTP request and to a cryptographic key, + which MSAL can manage. See https://aka.ms/msal-net-pop. + Set to true to enable PoP tokens automatically. + + + + + Options passed-in to create the token acquisition object which calls into MSAL .NET. + + + + + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + + + + + Returns the scopes. + + Scopes. + + + + Extension for IHttpClientBuilder for startup initialization of Microsoft Identity authentication handlers. + + + + + Adds a named Microsoft Identity user authentication message handler related to a specific configuration section. + + Builder. + Name of the configuration for the service. + Configuration. + The builder for chaining. + + + + Adds a named Microsoft Identity user authentication message handler initialized with delegates. + + Builder. + Name of the configuration for the service. + Action to configure the options. + The builder for chaining. + + + + Adds a named Microsoft Identity application authentication message handler related to a specific configuration section. + + Builder. + Name of the configuration for the service. + Configuration. + The builder for chaining. + + + + Adds a named Microsoft Identity application authentication message handler initialized with delegates. + + Builder. + Name of the configuration for the service. + Action to configure the options. + The builder for chaining. + + + + Options passed-in to Microsoft Identity message handlers. + + + + + Clone the options (to be able to override them). + + A clone of the options. + + + + Clone the options (to be able to override them). + + A clone of the options. + + + + A DelegatingHandler implementation that add an authorization header with a token on behalf of the current user. + + + + + Initializes a new instance of the class. + + Token acquisition service. + Named options provider. + Configuration options. + Name of the service describing the downstream web API. + + + + + + + Extension methods. + + + + Determines whether the specified string collection contains any. + The search for. + The string collection. + + true if the specified string collection contains any; otherwise, false. + + + + Keep the validated token associated with the HTTP request. + + HTTP context. + Token to preserve after the token is validated so that + it can be used in the actions. + + + + Get the parsed information about the token used to call the web API. + + HTTP context associated with the current request. + used to call the web API. + + + + Provides access to get or set the current error status. + The default implementation will use TempData and be enabled when run under Development. + + + + + Gets the error message for the current request. + + Current . + The current error message if available. + + + + Sets the error message for the current request. + + Current . + Error message to set. + + + + Gets whether error messages should be displayed. + + + + + Helper methods to handle incremental consent and conditional access in + a web app. + + + + + Can the exception be solved by re-signing-in the user?. + + Exception from which the decision will be made. + Returns true if the issue can be solved by signing-in + the user, and false, otherwise. + + + + Build authentication properties needed for incremental consent. + + Scopes to request. + instance. + User. + Userflow being invoked for AAD B2C. + AuthenticationProperties. + + + + An implementation of IConfigurationRetriever geared towards Azure AD issuers metadata. + + + + Retrieves a populated configuration given an address and an . + Address of the discovery document. + The to use to read the discovery document. + A cancellation token that can be used by other objects or threads to receive notice of cancellation. . + + A that, when completed, returns from the configuration. + + address - Azure AD Issuer metadata address URL is required + or retriever - No metadata document retriever is provided. + + + + Model class to hold information parsed from the Azure AD issuer endpoint. + + + + + Issuer associated with the OIDC endpoint. + + + + + Interface for the token acquisition service (encapsulating MSAL.NET). + + + + + Typically used from an ASP.NET Core web app or web API controller. This method gets an access token + for a downstream API on behalf of the user account for which the claims are provided in the + member of the controller's parameter. + + Scopes to request for the downstream API to call. + Enables to override the tenant/account for the same identity. This is useful in the + cases where a given account is guest in other tenants, and you want to acquire tokens for a specific tenant. + Azure AD B2C UserFlow to target. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a web API) + cases where a given account is guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An access token to call on behalf of the user, the downstream API characterized by its scopes. + + + + Typically used from an ASP.NET Core web app or web API controller. This method gets an access token + for a downstream API on behalf of the user account for which the claims are provided in the + member of the controller's parameter. + + Scopes to request for the downstream API to call. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + Enables to override the tenant/account for the same identity. This is useful in the + cases where a given account is guest in other tenants, and you want to acquire tokens for a specific tenant. + Azure AD B2C UserFlow to target. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a web API) + cases where a given account is guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An access token to call on behalf of the user, the downstream API characterized by its scopes. + + + + Typically used from an ASP.NET Core web app or web API controller. This method gets an access token + for a downstream API on behalf of the user account for which the claims are provided in the + member of the controller's parameter. + + Scopes to request for the downstream API to call. + Enables to override the tenant/account for the same identity. This is useful in the + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. + Azure AD B2C UserFlow to target. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a web API) + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An to call on behalf of the user, the downstream API characterized by its scopes. + + + + Typically used from an ASP.NET Core web app or web API controller. This method gets an access token + for a downstream API on behalf of the user account for which the claims are provided in the + member of the controller's parameter. + + Scopes to request for the downstream API to call. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. + Enables to override the tenant/account for the same identity. This is useful in the + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. + Azure AD B2C UserFlow to target. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a web API) + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An to call on behalf of the user, the downstream API characterized by its scopes. + + + + Acquires a token from the authority configured in the app, for the confidential client itself (not on behalf of a user) + using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + + The scope requested to access a protected API. For this flow (client credentials), the scope + should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft + Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration + in the portal, cannot be overridden in the application, as you can request a token for only one resource at a time (use + several calls to get tokens for other resources). + Enables overriding of the tenant/account for the same identity. This is useful in the + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An access token for the app itself, based on its scopes. + + + + Acquires a token from the authority configured in the app, for the confidential client itself (not on behalf of a user) + using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + + The scope requested to access a protected API. For this flow (client credentials), the scope + should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft + Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration + in the portal, cannot be overridden in the application, as you can request a token for only one resource at a time (use + several calls to get tokens for other resources). + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + Enables overriding of the tenant/account for the same identity. This is useful in the + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An access token for the app itself, based on its scopes. + + + + Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) + using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + + The scope requested to access a protected API. For this flow (client credentials), the scope + should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft + Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration + in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use + several calls to get tokens for other resources). + Enables overriding of the tenant/account for the same identity. This is useful + for multi tenant apps or daemons. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An authentication result for the app itself, based on its scopes. + + + + Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) + using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + + The scope requested to access a protected API. For this flow (client credentials), the scope + should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft + Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration + in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use + several calls to get tokens for other resources). + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + Enables overriding of the tenant/account for the same identity. This is useful + for multi tenant apps or daemons. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An authentication result for the app itself, based on its scopes. + + + + Used in web APIs (which therefore cannot have an interaction with the user). + Replies to the client through the HttpResponse by sending a 403 (forbidden) and populating wwwAuthenticateHeaders so that + the client can trigger an interaction with the user so the user can consent to more scopes. + + Scopes to consent to. + triggering the challenge. + The to update. + + + + Used in web APIs (which therefore cannot have an interaction with the user). + Replies to the client through the HttpResponse by sending a 403 (forbidden) and populating wwwAuthenticateHeaders so that + the client can trigger an interaction with the user so the user can consent to more scopes. + + Scopes to consent to. + triggering the challenge. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + The to update. + + + + Get the effective authentication scheme based on the context. + + Proposed authentication scheme. + Effective authenticationScheme which is the authentication scheme + if it's not null, or otherwise OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + + + + Used in web APIs (which therefore cannot have an interaction with the user). + Replies to the client through the HttpResponse by sending a 403 (forbidden) and populating wwwAuthenticateHeaders so that + the client can trigger an interaction with the user so the user can consent to more scopes. + + Scopes to consent to. + triggering the challenge. + The to update. + A representing the asynchronous operation. + + + + Interface for the internal operations of token acquisition service (encapsulating MSAL.NET). + + + + + In a web app, adds, to the MSAL.NET cache, the account of the user authenticating to the web app, when the authorization code is received (after the user + signed-in and consented) + An On-behalf-of token contained in the is added to the cache, so that it can then be used to acquire another token on-behalf-of the + same user in order to call to downstream APIs. + + The context used when an 'AuthorizationCode' is received over the OpenIdConnect protocol. + Scopes to request. + Authentication scheme to use. + A that represents a completed add to cache operation. + + From the configuration of the Authentication of the ASP.NET Core web API: + OpenIdConnectOptions options; + + Subscribe to the authorization code received event: + + options.Events = new OpenIdConnectEvents(); + options.Events.OnAuthorizationCodeReceived = OnAuthorizationCodeReceived; + } + + + And then in the OnAuthorizationCodeRecieved method, call : + + private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext context) + { + var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>(); + await _tokenAcquisition.AddAccountToCacheFromAuthorizationCode(context, new string[] { "user.read" }); + } + + + + + + Removes the account associated with context.HttpContext.User from the MSAL.NET cache. + + RedirectContext passed-in to a + OpenID Connect event. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. + A that represents a completed remove from cache operation. + + + + EventIds for Logging. + + + + + Options for configuring authentication using Azure Active Directory. It has both AAD and B2C configuration attributes. + Merges the MicrosoftIdentityWebOptions and the ConfidentialClientApplicationOptions. + + + + + Base class for web app and web API Microsoft Identity authentication + builders. + + + + + Constructor. + + The services being configured. + Optional configuration section. + + + + The services being configured. + + + + + Configuration section from which to bind options. + + It can be null if the configuration happens with delegates + rather than configuration. + + + + Extensions for IServerSideBlazorBuilder for startup initialization of web APIs. + + + + + Add the incremental consent and conditional access handler for Blazor + server side pages. + + Service side blazor builder. + The builder. + + + + Add the incremental consent and conditional access handler for + web app pages, Razor pages, controllers, views, etc... + + Service collection. + The service collection. + + + + Handler for Blazor specific APIs to handle incremental consent + and conditional access. + + + + + Initializes a new instance of the class. + + Service provider to get the HttpContextAccessor for the current HttpContext, when available. + + + + Boolean to determine if server is Blazor. + + + + + Current user. + + + + + Base URI to use in forming the redirect. + + + + + For Blazor/Razor pages to process the exception from + a user challenge. + + Exception. + + + + Forces the user to consent to specific scopes and perform + Conditional Access to get specific claims. Use on a Razor/Blazor + page or controller to proactively ensure the scopes and/or claims + before acquiring a token. The other mechanism + ensures claims and scopes requested by Azure AD after a failed token acquisition attempt. + See https://aka.ms/ms-id-web/ca_incremental-consent for details. + + Scopes to request. + Claims to ensure. + Userflow being invoked for AAD B2C. + + + + Options for configuring authentication using Azure Active Directory. It has both AAD and B2C configuration attributes. + + + + + Gets or sets the Azure Active Directory instance, e.g. "https://login.microsoftonline.com". + + + + + Gets or sets the tenant ID. + + + + + Gets or sets the domain of the Azure Active Directory tenant, e.g. contoso.onmicrosoft.com. + + + + + Gets or sets the edit profile user flow name for B2C, e.g. b2c_1_edit_profile. + + + + + Gets or sets the sign up or sign in user flow name for B2C, e.g. b2c_1_susi. + + + + + Gets or sets the reset password user flow name for B2C, e.g. B2C_1_password_reset. + + + + + Gets the default user flow (which is signUpsignIn). + + + + + Enables legacy ADAL cache serialization and deserialization. + Performance improvements when working with MSAL only apps. + Set to true if you have a shared cache with ADAL apps. + + The default is false. + + + + Is considered B2C if the attribute SignUpSignInPolicyId is defined. + + + + + Is considered to have client credentials if the attribute ClientCertificates + or ClientSecret is defined. + + + + + Description of the certificates used to prove the identity of the web app or web API. + For the moment only the first certificate is considered. + + An example in the appsetting.json: + + "ClientCertificates": [ + { + "SourceType": "StoreWithDistinguishedName", + "CertificateStorePath": "CurrentUser/My", + "CertificateDistinguishedName": "CN=WebAppCallingWebApiCert" + } + ] + + See also https://aka.ms/ms-id-web-certificates. + + + + + Description of the certificates used to decrypt an encrypted token in a web API. + For the moment only the first certificate is considered. + + An example in the appsetting.json: + + "TokenDecryptionCertificates": [ + { + "SourceType": "StoreWithDistinguishedName", + "CertificateStorePath": "CurrentUser/My", + "CertificateDistinguishedName": "CN=WebAppCallingWebApiCert" + } + ] + + See also https://aka.ms/ms-id-web-certificates. + + + + + Specifies if the x5c claim (public key of the certificate) should be sent to the STS. + Sending the x5c enables application developers to achieve easy certificate rollover in Azure AD: + this method will send the public certificate to Azure AD along with the token request, + so that Azure AD can use it to validate the subject name based on a trusted issuer policy. + This saves the application admin from the need to explicitly manage the certificate rollover + (either via portal or PowerShell/CLI operation). For details see https://aka.ms/msal-net-sni. + + The default is false. + + + + Daemon applications can validate a token based on roles, or using the ACL-based authorization + pattern to control tokens without a roles claim. If using ACL-based authorization, + Microsoft Identity Web will not throw if roles or scopes are not in the Claims. + For details see https://aka.ms/ms-identity-web/daemon-ACL. + + The default is false. + + + + Used, when deployed to Azure, to specify explicitly a user assigned managed identity. + See https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/how-to-manage-ua-identity-portal. + + + + + Microsoft Identity Web specific exception class for + use in Blazor or Razor pages to process the user challenge. + Handles the . + + + + + Exception thrown by MSAL when a user challenge is encountered. + + + + + Scopes to request. + + + + + Specified userflow. + + + + + Handles the user challenge for Blazor or Razor pages. + + Exception thrown by MSAL when a user challenge is encountered. + Scopes to request. + Userflow used in B2C. + + + + Generic class that validates token issuer from the provided Azure AD authority. + + + + + Validate the issuer for multi-tenant applications of various audiences (Work and School accounts, or Work and School accounts + + Personal accounts). + + Issuer to validate (will be tenanted). + Received security token. + Token validation parameters. + The issuer is considered as valid if it has the same HTTP scheme and authority as the + authority from the configuration file, has a tenant ID, and optionally v2.0 (this web API + accepts both V1 and V2 tokens). + The issuer if it's valid, or otherwise SecurityTokenInvalidIssuerException is thrown. + if is null. + if is null. + if the issuer is invalid. + + + Gets the tenant ID from a token. + A JWT token. + A string containing the tenant ID, if found or . + Only and are acceptable types. + + + + Interface implemented by diagnostics for the JWT Bearer middleware. + + + + + Called to subscribe to . + + JWT Bearer events. + The events (for chaining). + + + + Diagnostics used in the OpenID Connect middleware + (used in web apps). + + + + + Method to subscribe to . + + OpenID Connect events. + + + + Diagnostics for the JwtBearer middleware (used in web APIs). + + + + + Constructor for a . This constructor + is used by dependency injection. + + Logger. + + + + Invoked if exceptions are thrown during request processing. The exceptions will be re-thrown after this event unless suppressed. + + + + + Invoked when a protocol message is first received. + + + + + Invoked after the security token has passed validation and a ClaimsIdentity has been generated. + + + + + Invoked before a challenge is sent back to the caller. + + + + + Subscribes to all the JwtBearer events, to help debugging, while + preserving the previous handlers (which are called). + + Events to subscribe to. + for chaining. + + + + Factory class for creating the IssuerValidator per authority. + + + + + Initializes a new instance of the class. + + Options passed-in to create the AadIssuerValidator object. + HttpClientFactory. + + + + Gets an for an authority. + + The authority to create the validator for, e.g. https://login.microsoftonline.com/. + A for the aadAuthority. + if is null or empty. + + + + Diagnostics used in the OpenID Connect middleware + (used in web apps). + + + + + Constructor of the , used + by dependency injection. + + Logger used to log the diagnostics. + + + + Invoked before redirecting to the identity provider to authenticate. This can + be used to set ProtocolMessage.State that will be persisted through the authentication + process. The ProtocolMessage can also be used to add or customize parameters + sent to the identity provider. + + + + + Invoked when a protocol message is first received. + + + + + Invoked after security token validation if an authorization code is present + in the protocol message. + + + + + Invoked after "authorization code" is redeemed for tokens at the token endpoint. + + + + + Invoked when an IdToken has been validated and produced an AuthenticationTicket. + + + + + Invoked when user information is retrieved from the UserInfoEndpoint. + + + + + Invoked if exceptions are thrown during request processing. The exceptions will + be re-thrown after this event unless suppressed. + + + + + Invoked when a request is received on the RemoteSignOutPath. + + + + + Invoked before redirecting to the identity provider to sign out. + + + + + Invoked before redirecting to the Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions.SignedOutRedirectUri + at the end of a remote sign-out flow. + + + + + Subscribes to all the OpenIdConnect events, to help debugging, while + preserving the previous handlers (which are called). + + Events to subscribe to. + + + + Generic class that registers the token audience from the provided Azure AD authority. + + + + + Default validation of the audience: + - when registering an Azure AD web API in the app registration portal (and adding a scope) + the default App ID URI generated by the portal is api://{clientID} + - However, the audience (aud) of the token acquired to access this web API is different depending + on the "accepted access token version" for the web API: + - if accepted token version is 1.0, the audience provided in the token + by the Microsoft identity platform (formerly Azure AD v2.0) endpoint is: api://{ClientID} + - if the accepted token version is 2.0, the audience provided by Azure AD v2.0 in the token + is {CliendID} + When getting an access token for an Azure AD B2C web API the audience in the token is + api://{ClientID}. + + When web API developers don't provide the "Audience" in the configuration, Microsoft.Identity.Web + considers that this is the default App ID URI as explained above. When developer provides the + "Audience" member, it's available in the TokenValidationParameter.ValidAudience. + + Audiences in the security token. + Security token from which to validate the audiences. + Token validation parameters. + True if the token is valid; false, otherwise. + + + + This attribute is used on a controller, pages, or controller actions + to declare (and validate) the scopes required by a web API. These scopes can be declared + in two ways: hardcoding them, or declaring them in the configuration. Depending on your + choice, use either one or the other of the constructors. + For details, see https://aka.ms/ms-id-web/required-scope-attribute. + + + + + Fully qualified name of the configuration key containing the required scopes (separated + by spaces). + + + If the appsettings.json file contains a section named "AzureAd", in which + a property named "Scopes" contains the required scopes, the attribute on the + controller/page/action to protect should be set to the following: + + [RequiredScope(RequiredScopesConfigurationKey="AzureAd:Scopes")] + + + + + + Verifies that the web API is called with the right scopes. + If the token obtained for this API is on behalf of the authenticated user does not have + any of these in its scope claim, the + method updates the HTTP response providing a status code 403 (Forbidden) + and writes to the response body a message telling which scopes are expected in the token. + + Scopes accepted by this web API. + When the scopes don't match, the response is a 403 (Forbidden), + because the user is authenticated (hence not 401), but not authorized. + + Add the following attribute on the controller/page/action to protect: + + + [RequiredScope("access_as_user")] + + + and + if you want to express the required scopes from the configuration. + + - Old Object Id claim: http://schemas.microsoft.com/identity/claims/objectidentifier. + Default constructor, to be used along with the + property when you want to get the scopes to validate from the configuration, instead + of hardcoding them in the code. - + - New Object id claim: "oid". + If the authenticated user does not have any of these , the + method updates the HTTP response providing a status code 403 (Forbidden) + and writes to the response body a message telling which scopes are expected in the token. + Scopes accepted by this web API. + When the scopes don't match, the response is a 403 (Forbidden), + because the user is authenticated (hence not 401), but not authorized. - + - PreferredUserName: "preferred_username". + Extension class providing the extension methods for that + can be used in web APIs to validate the roles in controller actions. - + - Old TenantId claim: "http://schemas.microsoft.com/identity/claims/tenantid". + When applied to an , verifies that the application + has the expected roles. + HttpContext (from the controller). + Roles accepted by this web API. + When the roles don't match, the response is a 403 (Forbidden), + because the app does not have the expected roles. - + - New Tenant Id claim: "tid". + Extension class providing the extension + methods for that + can be used in web APIs to validate scopes in controller actions. + We recommend using instead the RequiredScope Attribute on the controller, the page or the action. + See https://aka.ms/ms-id-web/required-scope-attribute. - + - ClientInfo claim: "client_info". + When applied to an , verifies that the user authenticated in the + web API has any of the accepted scopes. + If there is no authenticated user, the response is a 401 (Unauthenticated). + If the authenticated user does not have any of these , the + method updates the HTTP response providing a status code 403 (Forbidden) + and writes to the response body a message telling which scopes are expected in the token. + We recommend using instead the RequiredScope Attribute on the controller, the page or the action. + See https://aka.ms/ms-id-web/required-scope-attribute. + HttpContext (from the controller). + Scopes accepted by this web API. - + - UniqueObjectIdentifier: "uid". - Home Object Id. + Options passed-in to create the AadIssuerValidator object. - + - UniqueTenantIdentifier: "utid". - Home Tenant Id. + Sets the name of the HttpClient to get from the IHttpClientFactory for use with the configuration manager. + Needed when customizing the client such as configuring a proxy. - + - Older scope claim: "http://schemas.microsoft.com/identity/claims/scope". + Extensions for IServiceCollection for startup initialization of web APIs. - - - Newer scope claim: "scp". - + + + Add the token acquisition service. + + Service collection. + Specifies if an instance of should be a singleton. + The service collection. + + This method is typically called from the ConfigureServices(IServiceCollection services) in Startup.cs. + Note that the implementation of the token cache can be chosen separately. + + + // Token acquisition service and its cache implementation as a session cache + services.AddTokenAcquisition() + .AddDistributedMemoryCache() + .AddSession() + .AddSessionBasedTokenCache(); + + - + - New Roles claim = "roles". + An implementation of that uses to track error messages. - + - Old Role claim: "http://schemas.microsoft.com/ws/2008/06/identity/claims/role". + Token acquisition service. - - - Subject claim: "sub". + LoggingMessage class for TokenAcquisition. - + - Acr claim: "acr". + Please call GetOrBuildConfidentialClientApplication instead of accessing this field directly. - + - UserFlow claim: "http://schemas.microsoft.com/claims/authnclassreference". + Constructor of the TokenAcquisition service. This requires the Azure AD Options to + configure the confidential client application and a token cache provider. + This constructor is called by ASP.NET Core dependency injection. + The App token cache provider. + Access to the HttpContext of the request. + Configuration options. + HTTP client factory. + Logger. + Service provider. - + - Tfp claim: "tfp". + Scopes which are already requested by MSAL.NET. They should not be re-requested;. - + - Name Identifier ID claim: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier". + Meta-tenant identifiers which are not allowed in client credentials. - - - General constants for Microsoft Identity Web. - + + + This handler is executed after the authorization code is received (once the user signs-in and consents) during the + authorization code flow in a web app. + It uses the code to request an access token from the Microsoft identity platform and caches the tokens and an entry about the signed-in user's account in the MSAL's token cache. + The access token (and refresh token) provided in the , once added to the cache, are then used to acquire more tokens using the + on-behalf-of flow for the signed-in user's account, + in order to call to downstream APIs. + + The context used when an 'AuthorizationCode' is received over the OpenIdConnect protocol. + scopes to request access to. + Authentication scheme to use (by default, OpenIdConnectDefaults.AuthenticationScheme). + + From the configuration of the Authentication of the ASP.NET Core web API: + OpenIdConnectOptions options; + + Subscribe to the authorization code received event: + + options.Events = new OpenIdConnectEvents(); + options.Events.OnAuthorizationCodeReceived = OnAuthorizationCodeReceived; + } + + + And then in the OnAuthorizationCodeRecieved method, call : + + private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext context) + { + var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>(); + await _tokenAcquisition.AddAccountToCacheFromAuthorizationCode(context, new string[] { "user.read" }); + } + + - + + + Typically used from a web app or web API controller, this method retrieves an access token + for a downstream API using; + 1) the token cache (for web apps and web APIs) if a token exists in the cache + 2) or the on-behalf-of flow + in web APIs, for the user account that is ascertained from claims provided in the + instance of the current HttpContext. + + Scopes to request for the downstream API to call. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. + Enables overriding of the tenant/account for the same identity. This is useful in the + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest. + Azure AD B2C user flow to target. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a web API) + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest. + Options passed-in to create the token acquisition options object which calls into MSAL .NET. + An access token to call the downstream API and populated with this downstream API's scopes. + Calling this method from a web API supposes that you have previously called, + in a method called by JwtBearerOptions.Events.OnTokenValidated, the HttpContextExtensions.StoreTokenUsedToCallWebAPI method + passing the validated token (as a JwtSecurityToken). Calling it from a web app supposes that + you have previously called AddAccountToCacheFromAuthorizationCodeAsync from a method called by + OpenIdConnectOptions.Events.OnAuthorizationCodeReceived. + + + + Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) + using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + + The scope requested to access a protected API. For this flow (client credentials), the scope + should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft + Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration + in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use + several calls to get tokens for other resources). + AuthenticationScheme to use. + Enables overriding of the tenant/account for the same identity. This is useful + for multi tenant apps or daemons. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An authentication result for the app itself, based on its scopes. + + + + Acquires a token from the authority configured in the app, for the confidential client itself (not on behalf of a user) + using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + + The scope requested to access a protected API. For this flow (client credentials), the scope + should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft + Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration + in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use + several calls to get tokens for other resources). + AuthenticationScheme to use. + Enables overriding of the tenant/account for the same identity. This is useful + for multi tenant apps or daemons. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An access token for the app itself, based on its scopes. + + + + Typically used from a web app or web API controller, this method retrieves an access token + for a downstream API using; + 1) the token cache (for web apps and web APIs) if a token exists in the cache + 2) or the on-behalf-of flow + in web APIs, for the user account that is ascertained from the claims provided in the + instance of the current HttpContext. + + Scopes to request for the downstream API to call. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + Enables overriding of the tenant/account for the same identity. This is useful in the + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. + Azure AD B2C user flow to target. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a web API) + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An access token to call the downstream API and populated with this downstream API's scopes. + Calling this method from a web API supposes that you have previously called, + in a method called by JwtBearerOptions.Events.OnTokenValidated, the HttpContextExtensions.StoreTokenUsedToCallWebAPI method + passing the validated token (as a JwtSecurityToken). Calling it from a web app supposes that + you have previously called AddAccountToCacheFromAuthorizationCodeAsync from a method called by + OpenIdConnectOptions.Events.OnAuthorizationCodeReceived. + + + + Used in web APIs (no user interaction). + Replies to the client through the HTTP response by sending a 403 (forbidden) and populating the 'WWW-Authenticate' header so that + the client, in turn, can trigger a user interaction so that the user consents to more scopes. + + Scopes to consent to. + The that triggered the challenge. + The to update. + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + + + + Used in web APIs (no user interaction). + Replies to the client through the HTTP response by sending a 403 (forbidden) and populating the 'WWW-Authenticate' header so that + the client, in turn, can trigger a user interaction so that the user consents to more scopes. + + Scopes to consent to. + The that triggered the challenge. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + The to update. + + + + Removes the account associated with context.HttpContext.User from the MSAL.NET cache. + + RedirectContext passed-in to a + OpenID Connect event. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + A that represents a completed account removal operation. + + + + + + + Creates an MSAL confidential client application. + + + + + Gets an access token for a downstream API on behalf of the user described by its claimsPrincipal. + + . + Claims principal for the user on behalf of whom to get a token. + Scopes for the downstream API to call. + (optional) Authority based on a specific tenant for which to acquire a token to access the scopes + on behalf of the user described in the claimsPrincipal. + Merged options. + Azure AD B2C user flow to target. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + + + + Gets an access token for a downstream API on behalf of the user whose account is passed as an argument. + + . + User IAccount for which to acquire a token. + See . + Scopes for the downstream API to call. + Authority based on a specific tenant for which to acquire a token to access the scopes + on behalf of the user. + Merged options. + Azure AD B2C user flow. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + + - LoginHint. - Represents the preferred_username claim in the ID token. + Logger for handling MSAL exceptions in TokenAcquisition. + ILogger. + Specific log message from TokenAcquisition. + Exception from MSAL.NET. - + - DomainHint. - Determined by the tenant Id. + Logger for handling information specific to MSAL in token acquisition. + ILogger. + durationTotalInMs. + durationInHttpInMs. + durationInCacheInMs. + cache or IDP. + correlationId. + Exception from MSAL.NET. - + - Claims. - Determined from the signed-in user. + Options passed-in to create the token acquisition object which calls into MSAL .NET. - + - Bearer. - Predominant type of access token used with OAuth 2.0. + Sets the correlation id to be used in the authentication request + to the /token endpoint. - + - AzureAd. - Configuration section name for AzureAd. + Sets Extra Query Parameters for the query string in the HTTP authentication request. - + - AzureAdB2C. - Configuration section name for AzureAdB2C. + A string with one or multiple claims to request. + Normally used with Conditional Access. - + - Scope. + Specifies if the token request will ignore the access token in the token cache + and will attempt to acquire a new access token. + If true, the request will ignore the token cache. The default is false. + Use this option with care and only when needed, for instance, if you know that conditional access policies have changed, + for it induces performance degradation, as the token cache is not utilized. - + - Policy for B2C user flows. - The name of the policy to check against a specific user flow. + Modifies the token acquisition request so that the acquired token is a Proof of Possession token (PoP), + rather than a Bearer token. + PoP tokens are similar to Bearer tokens, but are bound to the HTTP request and to a cryptographic key, + which MSAL can manage. See https://aka.ms/msal-net-pop. - + - Constants related to the error messages. + Cancellation token to be used when calling the token acquisition methods. - + - Constants related to the log messages. + Clone the options (to be able to override them). + A clone of the options. @@ -879,6 +3054,147 @@ Hints for the cache serialization implementation optimization. A that represents a completed remove key operation. + + + An implementation of token cache for confidential clients backed by an HTTP session. + + + For this session cache to work effectively, the ASP.NET Core session has to be configured properly. + The latest guidance is provided at https://docs.microsoft.com/aspnet/core/fundamentals/app-state + + In the method public void ConfigureServices(IServiceCollection services) in Startup.cs, add the following: + + services.AddSession(option => + { + option.Cookie.IsEssential = true; + }); + + In the method public void Configure(IApplicationBuilder app, IHostingEnvironment env) in Startup.cs, add the following: + + app.UseSession(); // Before UseMvc() + + + https://aka.ms/msal-net-token-cache-serialization + + LoggingMessage class for MsalSessionTokenCacheProvider. + + + + + MSAL Token cache provider constructor. + + Session for the current user. + Logger. + + + + Read a blob representing the token cache from its key. + + Key representing the token cache + (account or app). + Read blob. + + + + Read a blob representing the token cache from its key. + + Key representing the token cache + (account or app). + Hints for the cache serialization implementation optimization. + Read blob. + + + + Writes the token cache identified by its key to the serialization mechanism. + + Key for the cache (account ID or app ID). + Blob to write to the cache. + A that completes when a write operation has completed. + + + + Removes a cache described by its key. + + Key of the token cache (user account or app ID). + A that completes when key removal has completed. + + + + LoggingMessage class for MsalSessionTokenCacheProvider. + + + + + Session cache logging. + + ILogger. + /// Cache operation (Read, Write, etc...). + Session Id. + MSAL.NET cache key. + Exception. + + + + Session cache deserialized. + + ILogger. + MSAL.NET cache key. + Session Id. + Exception. + + + + Extension class to add a session token cache serializer to MSAL. + + + + + Adds an HTTP session-based application token cache to the service collection. + + + For this session cache to work effectively the ASP.NET Core session has to be configured properly. + The latest guidance is provided at https://docs.microsoft.com/aspnet/core/fundamentals/app-state. + + In the method public void ConfigureServices(IServiceCollection services) in Startup.cs, add the following: + + services.AddSession(option => + { + option.Cookie.IsEssential = true; + }); + + In the method public void Configure(IApplicationBuilder app, IHostingEnvironment env) in Startup.cs, add the following: + + app.UseSession(); // Before UseMvc() + + Because session token caches are added with scoped lifetime, they should not be used when TokenAcquisition is also used as a singleton (for example, when using Microsoft Graph SDK). + + The services collection to add to. + The service collection. + + + + Adds an HTTP session-based per-user token cache to the service collection. + + + For this session cache to work effectively the ASP.NET Core session has to be configured properly. + The latest guidance is provided at https://docs.microsoft.com/aspnet/core/fundamentals/app-state. + + In the method public void ConfigureServices(IServiceCollection services) in Startup.cs, add the following: + + services.AddSession(option => + { + option.Cookie.IsEssential = true; + }); + + In the method public void Configure(IApplicationBuilder app, IHostingEnvironment env) in Startup.cs, add the following: + + app.UseSession(); // Before UseMvc() + + Because session token caches are added with scoped lifetime, they should not be used when TokenAcquisition is also used as a singleton (for example, when using Microsoft Graph SDK). + + The services collection to add to. + The service collection. + Utility methods used by L1/L2 cache. @@ -985,89 +3301,281 @@ Don't use this method in ASP.NET Core. Just add use the ConfigureServices method instead. - + - Extensions for . + Authentication builder for a web API. - + - Gets the account identifier for an MSAL.NET account from a . + Constructor. - Claims principal. - A string corresponding to an account identifier as defined in . + The services being configured. + Default scheme used for OpenIdConnect. + ACtion called to configure the JwtBearer options. + Action called to configure + the Microsoft identity options. + Configuration section from which to + get parameters. - + - Gets the unique object ID associated with the . + Protects the web API with Microsoft identity platform (formerly Azure AD v2.0). - The from which to retrieve the unique object ID. - This method returns the object ID both in case the developer has enabled or not claims mapping. - Unique object ID of the identity, or null if it cannot be found. + The action to configure . + The authentication builder to chain. - + - Gets the Tenant ID associated with the . + Extensions for for startup initialization of web APIs. - The from which to retrieve the tenant ID. - Tenant ID of the identity, or null if it cannot be found. - This method returns the tenant ID both in case the developer has enabled or not claims mapping. - + - Gets the login-hint associated with a . + Protects the web API with Microsoft identity platform (formerly Azure AD v2.0). + This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. - Identity for which to complete the login-hint. - The login hint for the identity, or null if it cannot be found. + The to which to add this configuration. + The configuration instance. + The configuration section with the necessary settings to initialize authentication options. + The JWT bearer scheme name to be used. By default it uses "Bearer". + + Set to true if you want to debug, or just understand the JWT bearer events. + + The authentication builder to chain. - + - Gets the domain-hint associated with an identity. + Protects the web API with Microsoft identity platform (formerly Azure AD v2.0). + This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. - Identity for which to compute the domain-hint. - The domain hint for the identity, or null if it cannot be found. + The to which to add this configuration. + The configuration second from which to fill-in the options. + The JWT bearer scheme name to be used. By default it uses "Bearer". + + Set to true if you want to debug, or just understand the JWT bearer events. + + The authentication builder to chain. - + - Get the display name for the signed-in user, from the . + Protects the web API with Microsoft identity platform (formerly Azure AD v2.0). - Claims about the user/account. - A string containing the display name for the user, as determined by Azure AD (v1.0) and Microsoft identity platform (v2.0) tokens, - or null if the claims cannot be found. - See https://docs.microsoft.com/azure/active-directory/develop/id-tokens#payload-claims. + The to which to add this configuration. + The action to configure . + The action to configure the . + The JWT bearer scheme name to be used. By default it uses "Bearer". + + Set to true if you want to debug, or just understand the JWT bearer events. + The authentication builder to chain. - + - Gets the user flow ID associated with the . + Builder for web API authentication with configuration. - The from which to retrieve the user flow ID. - User flow ID of the identity, or null if it cannot be found. - + - Gets the Home Object ID associated with the . + Protects the web API with Microsoft identity platform (formerly Azure AD v2.0). + This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. - The from which to retrieve the sub claim. - Home Object ID (sub) of the identity, or null if it cannot be found. + The authentication builder to chain. - + - Gets the Home Tenant ID associated with the . + Extension for IServiceCollection for startup initialization of web APIs. - The from which to retrieve the sub claim. - Home Tenant ID (sub) of the identity, or null if it cannot be found. - + - Gets the NameIdentifierId associated with the . + Protects the web API with Microsoft identity platform (formerly Azure AD v2.0) + This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. - The from which to retrieve the NameIdentifierId claim. - Name identifier ID of the identity, or null if it cannot be found. + Service collection to which to add authentication. + The Configuration object. + The configuration section with the necessary settings to initialize authentication options. + The JwtBearer scheme name to be used. By default it uses "Bearer". + + Set to true if you want to debug, or just understand the JwtBearer events. + The authentication builder to chain extension methods. - + - EventIds for Logging. + Authentication builder returned by the EnableTokenAcquisitionToCallDownstreamApi methods + enabling you to decide token cache implementations. + + + + + Add in memory token caches. + + to configure. + to configure. + the service collection. + + + + Add distributed token caches. + + the service collection. + + + + Add session token caches. + + the service collection. + + + + Authentication builder specific for Microsoft identity platform. + + + + + Constructor. + + The services being configured. + Default scheme used for OpenIdConnect. + Action called to configure + the Microsoft identity options. + Optional configuration section. + + + + The web app calls a web API. + + Initial scopes. + The builder itself for chaining. + + + + The web app calls a web API. This override enables you to specify the + ConfidentialClientApplicationOptions (from MSAL.NET) programmatically. + + Action to configure the + MSAL.NET confidential client application options. + Initial scopes. + The builder itself for chaining. + + + + Extensions for the for startup initialization. + + + + + Add authentication to a web app with Microsoft identity platform. + This method expects the configuration file will have a section, named "AzureAd" as default, + with the necessary settings to initialize authentication options. + + The to which to add this configuration. + The configuration instance. + The configuration section with the necessary settings to initialize authentication options. + The OpenID Connect scheme name to be used. By default it uses "OpenIdConnect". + The cookie-based scheme name to be used. By default it uses "Cookies". + Set to true if you want to debug, or just understand the OpenID Connect events. + A display name for the authentication handler. + The builder for chaining. + + + + Add authentication with Microsoft identity platform. + This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. + + The to which to add this configuration. + The configuration section from which to get the options. + The OpenID Connect scheme name to be used. By default it uses "OpenIdConnect". + The cookie-based scheme name to be used. By default it uses "Cookies". + Set to true if you want to debug, or just understand the OpenID Connect events. + A display name for the authentication handler. + The authentication builder for chaining. + + + + Add authentication with Microsoft identity platform. + + The to which to add this configuration. + The action to configure . + The action to configure . + The OpenID Connect scheme name to be used. By default it uses "OpenIdConnect". + The cookie-based scheme name to be used. By default it uses "Cookies". + Set to true if you want to debug, or just understand the OpenID Connect events. + A display name for the authentication handler. + The authentication builder for chaining. + + + + Add authentication with Microsoft identity platform. + + The to which to add this configuration. + The action to configure . + The action to configure . + The OpenID Connect scheme name to be used. By default it uses "OpenIdConnect". + The cookie-based scheme name to be used. By default it uses "Cookies". + Set to true if you want to debug, or just understand the OpenID Connect events. + A display name for the authentication handler. + Configuration section. + The authentication builder for chaining. + + + + Add authentication with Microsoft identity platform. + + The to which to add this configuration. + The action to configure . + The action to configure . + The OpenID Connect scheme name to be used. By default it uses "OpenIdConnect". + The cookie-based scheme name to be used. By default it uses "Cookies". + Set to true if you want to debug, or just understand the OpenID Connect events. + A display name for the authentication handler. + The authentication builder for chaining. + + + + Builder for a Microsoft identity web app authentication where configuration is + available for EnableTokenAcquisitionToCallDownstreamApi. + + + + + Constructor. + + The services being configured. + Default scheme used for OpenIdConnect. + Action called to configure + the Microsoft identity options. + Optional configuration section. + + + + Add support for the web app to acquire tokens to call an API. + + Optional initial scopes to request. + The authentication builder for chaining. + + + + Extension for IServiceCollection for startup initialization. + + + + + Add authentication with Microsoft identity platform. + This method expects the configuration file will have a section, (by default named "AzureAd"), with the necessary settings to + initialize the authentication options. + Service collection to which to add authentication. + The IConfiguration object. + The name of the configuration section with the necessary + settings to initialize authentication options. + Optional name for the open id connect authentication scheme + (by default OpenIdConnectDefaults.AuthenticationScheme). This can be specified when you want to support + several OpenIdConnect identity providers. + Optional name for the cookie authentication scheme + (by default OpenIdConnectDefaults.AuthenticationScheme). + Set to true if you want to debug, or just understand the OpenIdConnect events. + A display name for the authentication handler. + The authentication builder to chain extension methods. diff --git a/src/Microsoft.Identity.Web/TokenCacheProviders/TokenCacheExtensions.cs b/src/Microsoft.Identity.Web/TokenCacheProviders/TokenCacheExtensions.cs index 26cecf1e3..e8e18d501 100644 --- a/src/Microsoft.Identity.Web/TokenCacheProviders/TokenCacheExtensions.cs +++ b/src/Microsoft.Identity.Web/TokenCacheProviders/TokenCacheExtensions.cs @@ -21,7 +21,7 @@ namespace Microsoft.Identity.Web /// public static class TokenCacheExtensions { - private static readonly IDictionary s_serviceProviderFromAction + private static readonly ConcurrentDictionary s_serviceProviderFromAction = new ConcurrentDictionary(); /// @@ -81,20 +81,23 @@ internal static IConfidentialClientApplication AddTokenCaches( throw new ArgumentNullException(nameof(initializeCaches)); } - // Maintain a dictionary of service providers per `initializeCaches` delegate. - if (!s_serviceProviderFromAction.TryGetValue(initializeCaches.Method, out IServiceProvider? serviceProvider)) + // try to reuse existing XYZ cache if AddXYZCache was called before, to simulate ASP.NET Core + var serviceProvider = s_serviceProviderFromAction.GetOrAdd(initializeCaches.Method, (m) => { - IHostBuilder hostBuilder = Host.CreateDefaultBuilder() - .ConfigureLogging(logger => { }) - .ConfigureServices(services => - { - initializeCaches(services); - services.AddDataProtection(); - }); + lock (s_serviceProviderFromAction) + { + IHostBuilder hostBuilder = Host.CreateDefaultBuilder() + .ConfigureLogging(logger => { }) + .ConfigureServices(services => + { + initializeCaches(services); + services.AddDataProtection(); + }); - serviceProvider = hostBuilder.Build().Services; - s_serviceProviderFromAction[initializeCaches.Method] = serviceProvider; - } + IServiceProvider sp = hostBuilder.Build().Services; + return sp; + } + }); IMsalTokenCacheProvider msalTokenCacheProvider = serviceProvider.GetRequiredService(); msalTokenCacheProvider.Initialize(confidentialClientApp.UserTokenCache); diff --git a/src/Microsoft.Identity.Web/Base64UrlHelpers.cs b/src/Microsoft.Identity.Web/Util/Base64UrlHelpers.cs similarity index 97% rename from src/Microsoft.Identity.Web/Base64UrlHelpers.cs rename to src/Microsoft.Identity.Web/Util/Base64UrlHelpers.cs index f644eca36..351090b98 100644 --- a/src/Microsoft.Identity.Web/Base64UrlHelpers.cs +++ b/src/Microsoft.Identity.Web/Util/Base64UrlHelpers.cs @@ -2,10 +2,9 @@ // Licensed under the MIT License. using System; -using System.Globalization; using System.Text; -namespace Microsoft.Identity.Web +namespace Microsoft.Identity.Web.Util { internal static class Base64UrlHelpers { diff --git a/tests/Microsoft.Identity.Web.Test.Common/Microsoft.Identity.Web.Test.Common.csproj b/tests/Microsoft.Identity.Web.Test.Common/Microsoft.Identity.Web.Test.Common.csproj index 37f8e4cbe..16df3b411 100644 --- a/tests/Microsoft.Identity.Web.Test.Common/Microsoft.Identity.Web.Test.Common.csproj +++ b/tests/Microsoft.Identity.Web.Test.Common/Microsoft.Identity.Web.Test.Common.csproj @@ -17,6 +17,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/Microsoft.Identity.Web.Test.Common/Mocks/MockHttpClientFactory.cs b/tests/Microsoft.Identity.Web.Test.Common/Mocks/MockHttpClientFactory.cs new file mode 100644 index 000000000..1d2bac9f4 --- /dev/null +++ b/tests/Microsoft.Identity.Web.Test.Common/Mocks/MockHttpClientFactory.cs @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Runtime.InteropServices; +using Microsoft.Identity.Client; +using Xunit; + +namespace Microsoft.Identity.Web.Test.Common.Mocks +{ + public class MockHttpClientFactory : IMsalHttpClientFactory, IDisposable + { + /// + public void Dispose() + { + // This ensures we only check the mock queue on dispose when we're not in the middle of an + // exception flow. Otherwise, any early assertion will cause this to likely fail + // even though it's not the root cause. +#pragma warning disable CS0618 // Type or member is obsolete - this is non-production code so it's fine + if (Marshal.GetExceptionCode() == 0) +#pragma warning restore CS0618 // Type or member is obsolete + { + string remainingMocks = string.Join( + " ", + _httpMessageHandlerQueue.Select( + h => (h as MockHttpMessageHandler)?.ExpectedUrl ?? string.Empty)); + + Assert.Empty(_httpMessageHandlerQueue); + } + } + + public MockHttpMessageHandler AddMockHandler(MockHttpMessageHandler handler) + { + _httpMessageHandlerQueue.Enqueue(handler); + return handler; + } + + private Queue _httpMessageHandlerQueue = new Queue(); + + public HttpClient GetHttpClient() + { + HttpMessageHandler messageHandler; + + Assert.NotEmpty(_httpMessageHandlerQueue); + messageHandler = _httpMessageHandlerQueue.Dequeue(); + + var httpClient = new HttpClient(messageHandler); + + httpClient.DefaultRequestHeaders.Accept.Clear(); + httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + + return httpClient; + } + } +} diff --git a/tests/Microsoft.Identity.Web.Test.Common/Mocks/MockHttpCreator.cs b/tests/Microsoft.Identity.Web.Test.Common/Mocks/MockHttpCreator.cs new file mode 100644 index 000000000..13c459317 --- /dev/null +++ b/tests/Microsoft.Identity.Web.Test.Common/Mocks/MockHttpCreator.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Net; +using System.Net.Http; +using Microsoft.Identity.Web.Test.Common; +using Microsoft.Identity.Web.Util; + +namespace Microsoft.Identity.Web.Test.Common.Mocks +{ + public static class MockHttpCreator + { + private static HttpResponseMessage CreateSuccessfulClientCredentialTokenResponseMessage(string token = "header.payload.signature", string expiry = "3599") + { + return CreateSuccessResponseMessage( + "{\"token_type\":\"Bearer\",\"expires_in\":\"" + expiry + "\",\"client_info\":\"" + CreateClientInfo() + "\",\"access_token\":\"" + token + "\"}"); + } + + public static HttpResponseMessage CreateSuccessResponseMessage(string successResponse) + { + HttpResponseMessage responseMessage = new HttpResponseMessage(HttpStatusCode.OK); + HttpContent content = + new StringContent(successResponse); + responseMessage.Content = content; + return responseMessage; + } + + private static string CreateClientInfo(string uid = TestConstants.Uid, string utid = TestConstants.Utid) + { + return Base64UrlHelpers.Encode("{\"uid\":\"" + uid + "\",\"utid\":\"" + utid + "\"}"); + } + + public static MockHttpMessageHandler CreateInstanceDiscoveryMockHandler( + string discoveryEndpoint = "https://login.microsoftonline.com/common/discovery/instance", + string content = TestConstants.DiscoveryJsonResponse) + { + return new MockHttpMessageHandler() + { + ExpectedUrl = discoveryEndpoint, + ExpectedMethod = HttpMethod.Get, + ResponseMessage = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(content), + }, + }; + } + + public static MockHttpMessageHandler CreateClientCredentialTokenHandler( + string token = "header.payload.signature", string expiresIn = "3599") + { + var handler = new MockHttpMessageHandler() + { + ExpectedMethod = HttpMethod.Post, + ResponseMessage = CreateSuccessfulClientCredentialTokenResponseMessage(token, expiresIn), + }; + + return handler; + } + } +} diff --git a/tests/Microsoft.Identity.Web.Test.Common/Mocks/MockHttpMessageHandler.cs b/tests/Microsoft.Identity.Web.Test.Common/Mocks/MockHttpMessageHandler.cs new file mode 100644 index 000000000..968448c77 --- /dev/null +++ b/tests/Microsoft.Identity.Web.Test.Common/Mocks/MockHttpMessageHandler.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.Identity.Web.Test.Common.Mocks +{ + public class MockHttpMessageHandler : HttpMessageHandler + { + public HttpResponseMessage ResponseMessage { get; set; } + public string ExpectedUrl { get; set; } + + public HttpMethod ExpectedMethod { get; set; } + + public Exception ExceptionToThrow { get; set; } + + /// + /// Once the http message is executed, this property holds the request message + /// + public HttpRequestMessage ActualRequestMessage { get; private set; } + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + ActualRequestMessage = request; + + if (ExceptionToThrow != null) + { + throw ExceptionToThrow; + } + + var uri = request.RequestUri; + if (!string.IsNullOrEmpty(ExpectedUrl)) + { + Assert.Equal( + ExpectedUrl, + uri.AbsoluteUri.Split( + new[] + { + '?', + })[0]); + } + + Assert.Equal(ExpectedMethod, request.Method); + + if (request.Method != HttpMethod.Get && request.Content != null) + { + string postData = request.Content.ReadAsStringAsync().Result; + } + + return new TaskFactory().StartNew(() => ResponseMessage, cancellationToken); + } + } +} diff --git a/tests/Microsoft.Identity.Web.Test.Common/TestConstants.cs b/tests/Microsoft.Identity.Web.Test.Common/TestConstants.cs index 0c280018c..9e3f32be0 100644 --- a/tests/Microsoft.Identity.Web.Test.Common/TestConstants.cs +++ b/tests/Microsoft.Identity.Web.Test.Common/TestConstants.cs @@ -169,5 +169,42 @@ public static class TestConstants public const string ActualIssuer = "actualIssuer"; public const string SecurityToken = "securityToken"; public const string ValidationParameters = "validationParameters"; + + public const string DiscoveryJsonResponse = @"{ + ""tenant_discovery_endpoint"":""https://login.microsoftonline.com/tenant/.well-known/openid-configuration"", + ""api-version"":""1.1"", + ""metadata"":[ + { + ""preferred_network"":""login.microsoftonline.com"", + ""preferred_cache"":""login.windows.net"", + ""aliases"":[ + ""login.microsoftonline.com"", + ""login.windows.net"", + ""login.microsoft.com"", + ""sts.windows.net""]}, + { + ""preferred_network"":""login.partner.microsoftonline.cn"", + ""preferred_cache"":""login.partner.microsoftonline.cn"", + ""aliases"":[ + ""login.partner.microsoftonline.cn"", + ""login.chinacloudapi.cn""]}, + { + ""preferred_network"":""login.microsoftonline.de"", + ""preferred_cache"":""login.microsoftonline.de"", + ""aliases"":[ + ""login.microsoftonline.de""]}, + { + ""preferred_network"":""login.microsoftonline.us"", + ""preferred_cache"":""login.microsoftonline.us"", + ""aliases"":[ + ""login.microsoftonline.us"", + ""login.usgovcloudapi.net""]}, + { + ""preferred_network"":""login-us.microsoftonline.com"", + ""preferred_cache"":""login-us.microsoftonline.com"", + ""aliases"":[ + ""login-us.microsoftonline.com""]} + ] + }"; } } diff --git a/tests/Microsoft.Identity.Web.Test/Base64UrlHelpersTests.cs b/tests/Microsoft.Identity.Web.Test/Base64UrlHelpersTests.cs index 71f150860..c2ef2c593 100644 --- a/tests/Microsoft.Identity.Web.Test/Base64UrlHelpersTests.cs +++ b/tests/Microsoft.Identity.Web.Test/Base64UrlHelpersTests.cs @@ -3,6 +3,7 @@ using System; using System.Text; +using Microsoft.Identity.Web.Util; using Xunit; namespace Microsoft.Identity.Web.Test diff --git a/tests/Microsoft.Identity.Web.Test/CacheExtensionsTests.cs b/tests/Microsoft.Identity.Web.Test/CacheExtensionsTests.cs index 07b423568..ec84ae99b 100644 --- a/tests/Microsoft.Identity.Web.Test/CacheExtensionsTests.cs +++ b/tests/Microsoft.Identity.Web.Test/CacheExtensionsTests.cs @@ -2,9 +2,12 @@ // Licensed under the MIT License. using System; +using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Identity.Client; using Microsoft.Identity.Web.Test.Common; +using Microsoft.Identity.Web.Test.Common.Mocks; using Xunit; namespace Microsoft.Identity.Web.Test @@ -23,6 +26,81 @@ public void InMemoryCacheExtensionsTests() Assert.NotNull(_confidentialApp.AppTokenCache); } + [Fact] + // bug: https://github.com/AzureAD/microsoft-identity-web/issues/1390 + public async Task InMemoryCacheExtensionsAgainTestsAsync() + { + AuthenticationResult result; + result = await CreateAppAndGetTokenAsync(CacheType.InMemory).ConfigureAwait(false); + Assert.Equal(TokenSource.IdentityProvider, result.AuthenticationResultMetadata.TokenSource); + + result = await CreateAppAndGetTokenAsync(CacheType.InMemory, false, false).ConfigureAwait(false); + Assert.Equal(TokenSource.Cache, result.AuthenticationResultMetadata.TokenSource); + + result = await CreateAppAndGetTokenAsync(CacheType.DistributedInMemory, true, false).ConfigureAwait(false); + Assert.Equal(TokenSource.IdentityProvider, result.AuthenticationResultMetadata.TokenSource); + + result = await CreateAppAndGetTokenAsync(CacheType.DistributedInMemory, false, false).ConfigureAwait(false); + Assert.Equal(TokenSource.Cache, result.AuthenticationResultMetadata.TokenSource); + } + + private enum CacheType + { + InMemory, + DistributedInMemory, + } + + private static async Task CreateAppAndGetTokenAsync( + CacheType cacheType, + bool addTokenMock = true, + bool addInstanceMock = true) + { + using (MockHttpClientFactory mockHttp = new MockHttpClientFactory()) + using (var discoveryHandler = MockHttpCreator.CreateInstanceDiscoveryMockHandler()) + using (var tokenHandler = MockHttpCreator.CreateClientCredentialTokenHandler()) + { + + if (addInstanceMock) + { + mockHttp.AddMockHandler(discoveryHandler); + } + + // for when the token is requested from ESTS + if (addTokenMock) + { + mockHttp.AddMockHandler(tokenHandler); + } + + var confidentialApp = ConfidentialClientApplicationBuilder + .Create(TestConstants.ClientId) + .WithAuthority(TestConstants.AuthorityCommonTenant) + .WithHttpClientFactory(mockHttp) + .WithClientSecret(TestConstants.ClientSecret) + .Build(); + + switch (cacheType) + { + case CacheType.InMemory: + confidentialApp.AddInMemoryTokenCache(); + break; + + case CacheType.DistributedInMemory: + confidentialApp.AddDistributedTokenCache(services => + { + services.AddDistributedMemoryCache(); + services.AddLogging(configure => configure.AddConsole()) + .Configure(options => options.MinLevel = Microsoft.Extensions.Logging.LogLevel.Warning); + }); + break; + } + + var result = await confidentialApp.AcquireTokenForClient(new[] { TestConstants.s_scopeForApp }) + .ExecuteAsync().ConfigureAwait(false); + + return result; + } + } + [Fact] public void InMemoryCacheExtensions_NoCca_ThrowsException_Tests() { diff --git a/tests/Microsoft.Identity.Web.Test/ClientInfoTests.cs b/tests/Microsoft.Identity.Web.Test/ClientInfoTests.cs index 8079c9fa7..5ae5d5b31 100644 --- a/tests/Microsoft.Identity.Web.Test/ClientInfoTests.cs +++ b/tests/Microsoft.Identity.Web.Test/ClientInfoTests.cs @@ -5,6 +5,7 @@ using System.Text; using System.Text.Json; using Microsoft.Identity.Web.Test.Common; +using Microsoft.Identity.Web.Util; using Xunit; namespace Microsoft.Identity.Web.Test diff --git a/tests/Microsoft.Identity.Web.Test/WebAppExtensionsTests.cs b/tests/Microsoft.Identity.Web.Test/WebAppExtensionsTests.cs index 84c14f5f8..91edb30bd 100644 --- a/tests/Microsoft.Identity.Web.Test/WebAppExtensionsTests.cs +++ b/tests/Microsoft.Identity.Web.Test/WebAppExtensionsTests.cs @@ -25,6 +25,7 @@ using Microsoft.Identity.Web.Test.Common.TestHelpers; using Microsoft.Identity.Web.TokenCacheProviders; using Microsoft.Identity.Web.TokenCacheProviders.InMemory; +using Microsoft.Identity.Web.Util; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using NSubstitute; using NSubstitute.Extensions;