internal static void WebAppCallsWebApiImplementation( IServiceCollection services, IEnumerable <string>?initialScopes, Action <MicrosoftIdentityOptions> configureMicrosoftIdentityOptions, string openIdConnectScheme, Action <ConfidentialClientApplicationOptions> configureConfidentialClientApplicationOptions) { // Ensure that configuration options for MSAL.NET, HttpContext accessor and the Token acquisition service // (encapsulating MSAL.NET) are available through dependency injection services.Configure(configureMicrosoftIdentityOptions); services.Configure(configureConfidentialClientApplicationOptions); services.AddHttpContextAccessor(); services.AddTokenAcquisition(); services.AddOptions <OpenIdConnectOptions>(openIdConnectScheme) .Configure <IServiceProvider>((options, serviceProvider) => { options.ResponseType = OpenIdConnectResponseType.Code; options.UsePkce = false; // This scope is needed to get a refresh token when users sign-in with their Microsoft personal accounts // It's required by MSAL.NET and automatically provided when users sign-in with work or school accounts options.Scope.Add(OidcConstants.ScopeOfflineAccess); if (initialScopes != null) { foreach (string scope in initialScopes) { if (!options.Scope.Contains(scope)) { options.Scope.Add(scope); } } } // Handling the auth redemption by MSAL.NET so that a token is available in the token cache // where it will be usable from Controllers later (through the TokenAcquisition service) var codeReceivedHandler = options.Events.OnAuthorizationCodeReceived; options.Events.OnAuthorizationCodeReceived = async context => { var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService <ITokenAcquisitionInternal>(); await tokenAcquisition.AddAccountToCacheFromAuthorizationCodeAsync(context, options.Scope).ConfigureAwait(false); await codeReceivedHandler(context).ConfigureAwait(false); }; // Handling the token validated to get the client_info for cases where tenantId is not present (example: B2C) var onTokenValidatedHandler = options.Events.OnTokenValidated; options.Events.OnTokenValidated = async context => { string?clientInfo = context.ProtocolMessage?.GetParameter(ClaimConstants.ClientInfo); if (!string.IsNullOrEmpty(clientInfo)) { ClientInfo?clientInfoFromServer = ClientInfo.CreateFromJson(clientInfo); if (clientInfoFromServer != null) { context.Principal.Identities.FirstOrDefault()?.AddClaim(new Claim(ClaimConstants.UniqueTenantIdentifier, clientInfoFromServer.UniqueTenantIdentifier)); context.Principal.Identities.FirstOrDefault()?.AddClaim(new Claim(ClaimConstants.UniqueObjectIdentifier, clientInfoFromServer.UniqueObjectIdentifier)); } } await onTokenValidatedHandler(context).ConfigureAwait(false); }; // Handling the sign-out: removing the account from MSAL.NET cache var signOutHandler = options.Events.OnRedirectToIdentityProviderForSignOut; options.Events.OnRedirectToIdentityProviderForSignOut = async context => { // Remove the account from MSAL.NET token cache var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService <ITokenAcquisitionInternal>(); await tokenAcquisition.RemoveAccountAsync(context).ConfigureAwait(false); await signOutHandler(context).ConfigureAwait(false); }; }); }