diff --git a/src/Microsoft.Identity.Web.UI/Areas/MicrosoftIdentity/Controllers/AccountController.cs b/src/Microsoft.Identity.Web.UI/Areas/MicrosoftIdentity/Controllers/AccountController.cs index ff175ad8d..c0d019c8d 100644 --- a/src/Microsoft.Identity.Web.UI/Areas/MicrosoftIdentity/Controllers/AccountController.cs +++ b/src/Microsoft.Identity.Web.UI/Areas/MicrosoftIdentity/Controllers/AccountController.cs @@ -66,7 +66,7 @@ public IActionResult Challenge( string claims) { string scheme = OpenIdConnectDefaults.AuthenticationScheme; - Dictionary properties = new Dictionary + Dictionary properties = new Dictionary { { Constants.Scope, scope }, { Constants.LoginHint, loginHint }, diff --git a/src/Microsoft.Identity.Web/Constants/IDWebErrorMessage.cs b/src/Microsoft.Identity.Web/Constants/IDWebErrorMessage.cs index 50af2fb8f..3d40907d8 100644 --- a/src/Microsoft.Identity.Web/Constants/IDWebErrorMessage.cs +++ b/src/Microsoft.Identity.Web/Constants/IDWebErrorMessage.cs @@ -8,8 +8,8 @@ namespace Microsoft.Identity.Web /// internal static class IDWebErrorMessage { - // General - // public const string IDW10000 = "IDW10000:"; + // General IDW10000 = "IDW10000:" + public const string HttpContextIsNull = "IDW10000: HttpContext is null. "; // Configuration IDW10100 = "IDW10100:" public const string ProvideEitherScopeKeySectionOrScopes = "IDW10101: Either provide the '{0}' or the '{1}' to the 'AuthorizeForScopes'. "; diff --git a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml index 9dfc6abb8..dd6b8ec77 100644 --- a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml +++ b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml @@ -718,12 +718,12 @@ - Extensions for IServiceCollection for startup initialization of web APIs. + Extensions for IServerSideBlazorBuilder for startup initialization of web APIs. - Add the incremental consent and conditional access handler for blazor + Add the incremental consent and conditional access handler for Blazor server side pages. Service side blazor builder. @@ -747,7 +747,7 @@ - Base uri to use in forming the redirect. + Base URI to use in forming the redirect. @@ -1863,11 +1863,11 @@ - Adds both App and per-user session token caches. + Adds both application and per-user session token caches. 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 + 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: @@ -1880,17 +1880,18 @@ 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 both App and per-user session token caches. + Adds both application and per-user session token caches. 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 + 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: @@ -1903,17 +1904,18 @@ 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 authentication builder to add the session token caches to. The builder to chain more commands. - Adds an HTTP session based application token cache to the service collection. + 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 + 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: @@ -1926,17 +1928,18 @@ 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. + 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 + 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: @@ -1949,6 +1952,7 @@ 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. diff --git a/src/Microsoft.Identity.Web/MicrosoftIdentityCircuitHandler.cs b/src/Microsoft.Identity.Web/MicrosoftIdentityCircuitHandler.cs index c3ce01922..94a5b29c7 100644 --- a/src/Microsoft.Identity.Web/MicrosoftIdentityCircuitHandler.cs +++ b/src/Microsoft.Identity.Web/MicrosoftIdentityCircuitHandler.cs @@ -15,12 +15,12 @@ namespace Microsoft.Identity.Web { /// - /// Extensions for IServiceCollection for startup initialization of web APIs. + /// Extensions for IServerSideBlazorBuilder for startup initialization of web APIs. /// public static class MicrosoftIdentityBlazorServiceCollectionExtensions { /// - /// Add the incremental consent and conditional access handler for blazor + /// Add the incremental consent and conditional access handler for Blazor /// server side pages. /// /// Service side blazor builder. @@ -55,10 +55,10 @@ public class MicrosoftIdentityConsentAndConditionalAccessHandler /// /// Current user. /// - public ClaimsPrincipal User { get; internal set; } + public ClaimsPrincipal User { get; internal set; } = null!; /// - /// Base uri to use in forming the redirect. + /// Base URI to use in forming the redirect. /// public string? BaseUri { get; internal set; } @@ -88,13 +88,13 @@ public void HandleException(Exception exception) User); string redirectUri = NavigationManager.Uri; - List scope = properties.Parameters.ContainsKey(Constants.Scope) ? (List)properties.Parameters[Constants.Scope] : new List(); - string loginHint = properties.Parameters.ContainsKey(Constants.LoginHint) ? (string)properties.Parameters[Constants.LoginHint] : string.Empty; - string domainHint = properties.Parameters.ContainsKey(Constants.DomainHint) ? (string)properties.Parameters[Constants.DomainHint] : string.Empty; - string claims = properties.Parameters.ContainsKey(Constants.Claims) ? (string)properties.Parameters[Constants.Claims] : string.Empty; + List scope = properties.Parameters.ContainsKey(Constants.Scope) ? (List)properties.Parameters[Constants.Scope]! : new List(); + string loginHint = properties.Parameters.ContainsKey(Constants.LoginHint) ? (string)properties.Parameters[Constants.LoginHint]! : string.Empty; + string domainHint = properties.Parameters.ContainsKey(Constants.DomainHint) ? (string)properties.Parameters[Constants.DomainHint]! : string.Empty; + string claims = properties.Parameters.ContainsKey(Constants.Claims) ? (string)properties.Parameters[Constants.Claims]! : string.Empty; string url = $"{NavigationManager.BaseUri}{Constants.BlazorChallengeUri}{redirectUri}" - + $"&{Constants.Scope}={string.Join(" ", scope)}&{Constants.LoginHint}={loginHint}" - + $"&{Constants.DomainHint}={domainHint}&{Constants.Claims}={claims}"; + + $"&{Constants.Scope}={string.Join(" ", scope!)}&{Constants.LoginHint}={loginHint}" + + $"&{Constants.DomainHint}={domainHint}&{Constants.Claims}={claims}"; NavigationManager.NavigateTo(url, true); } @@ -104,9 +104,7 @@ public void HandleException(Exception exception) } } -#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. - internal NavigationManager NavigationManager { get; set; } -#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. + internal NavigationManager NavigationManager { get; set; } = null!; } #pragma warning disable SA1402 // File may only contain a single type diff --git a/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs b/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs index e4c0e8458..4b1f0bed9 100644 --- a/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs +++ b/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs @@ -61,12 +61,12 @@ public static IServiceCollection AddTokenAcquisition( if (isTokenAcquisitionSingleton) { services.AddSingleton(); - services.AddSingleton(s => (ITokenAcquisitionInternal)s.GetService()); + services.AddSingleton(s => (ITokenAcquisitionInternal)s.GetRequiredService()); } else { services.AddScoped(); - services.AddScoped(s => (ITokenAcquisitionInternal)s.GetService()); + services.AddScoped(s => (ITokenAcquisitionInternal)s.GetRequiredService()); } return services; diff --git a/src/Microsoft.Identity.Web/TokenAcquisition.cs b/src/Microsoft.Identity.Web/TokenAcquisition.cs index fc0c54ec3..85d6e6a84 100644 --- a/src/Microsoft.Identity.Web/TokenAcquisition.cs +++ b/src/Microsoft.Identity.Web/TokenAcquisition.cs @@ -36,7 +36,7 @@ internal class TokenAcquisition : ITokenAcquisition, ITokenAcquisitionInternal private IConfidentialClientApplication? _application; private readonly IHttpContextAccessor _httpContextAccessor; - private HttpContext CurrentHttpContext => _httpContextAccessor.HttpContext; + private HttpContext? CurrentHttpContext => _httpContextAccessor.HttpContext; private readonly IMsalHttpClientFactory _httpClientFactory; private readonly ILogger _logger; private readonly IServiceProvider _serviceProvider; @@ -270,9 +270,9 @@ public async Task GetAccessTokenForUserAsync( // GetAccessTokenForUserAsync is an abstraction that can be called from a web app or a web API _logger.LogInformation(ex.Message); - // To get a token for a web API on behalf of the user, but not necessarily with the on behalf of OAuth2.0 - // flow as this one only applies to web APIs. - JwtSecurityToken? validatedToken = CurrentHttpContext.GetTokenUsedToCallWebAPI(); + // to get a token for a Web API on behalf of the user, but not necessarily with the on behalf of OAuth2.0 + // flow as this one only applies to Web APIs. + JwtSecurityToken? validatedToken = CurrentHttpContext?.GetTokenUsedToCallWebAPI(); // Case of web APIs: we need to do an on-behalf-of flow if (validatedToken != null) diff --git a/src/Microsoft.Identity.Web/TokenCacheProviders/Session/SessionTokenCacheProviderExtension.cs b/src/Microsoft.Identity.Web/TokenCacheProviders/Session/SessionTokenCacheProviderExtension.cs index 0d493efa4..685ecf43a 100644 --- a/src/Microsoft.Identity.Web/TokenCacheProviders/Session/SessionTokenCacheProviderExtension.cs +++ b/src/Microsoft.Identity.Web/TokenCacheProviders/Session/SessionTokenCacheProviderExtension.cs @@ -17,11 +17,11 @@ namespace Microsoft.Identity.Web.TokenCacheProviders.Session public static class SessionTokenCacheProviderExtension { /// - /// Adds both App and per-user session token caches. + /// Adds both application and per-user session token caches. /// /// /// 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 + /// 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: /// @@ -34,6 +34,7 @@ public static class SessionTokenCacheProviderExtension /// /// 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. @@ -66,17 +67,26 @@ public static IServiceCollection AddSessionTokenCaches(this IServiceCollection s services.AddHttpContextAccessor(); services.AddScoped(); - services.TryAddScoped(provider => provider.GetService().HttpContext.Session); + services.TryAddScoped(provider => + { + var httpContext = provider.GetRequiredService().HttpContext; + if (httpContext == null) + { + throw new InvalidOperationException(IDWebErrorMessage.HttpContextIsNull); + } + + return httpContext.Session; + }); return services; } /// - /// Adds both App and per-user session token caches. + /// Adds both application and per-user session token caches. /// /// /// 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 + /// 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: /// @@ -89,6 +99,7 @@ public static IServiceCollection AddSessionTokenCaches(this IServiceCollection s /// /// 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 authentication builder to add the session token caches to. /// The builder to chain more commands. @@ -104,11 +115,11 @@ public static AuthenticationBuilder AddSessionTokenCaches(this AuthenticationBui } /// - /// Adds an HTTP session based application token cache to the service collection. + /// 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 + /// 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: /// @@ -121,6 +132,7 @@ public static AuthenticationBuilder AddSessionTokenCaches(this AuthenticationBui /// /// 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. @@ -137,17 +149,26 @@ public static IServiceCollection AddSessionAppTokenCache(this IServiceCollection option.Cookie.IsEssential = true; }); services.AddScoped(); - services.TryAddScoped(provider => provider.GetService().HttpContext.Session); + services.TryAddScoped(provider => + { + var httpContext = provider.GetRequiredService().HttpContext; + if (httpContext == null) + { + throw new InvalidOperationException(IDWebErrorMessage.HttpContextIsNull); + } + + return httpContext.Session; + }); return services; } /// - /// Adds an HTTP session based per user token cache 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 + /// 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: /// @@ -160,6 +181,7 @@ public static IServiceCollection AddSessionAppTokenCache(this IServiceCollection /// /// 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. @@ -172,11 +194,20 @@ public static IServiceCollection AddSessionPerUserTokenCache(this IServiceCollec services.AddHttpContextAccessor(); services.AddSession(option => - { - option.Cookie.IsEssential = true; - }); + { + option.Cookie.IsEssential = true; + }); services.AddScoped(); - services.TryAddScoped(provider => provider.GetService().HttpContext.Session); + services.TryAddScoped(provider => + { + var httpContext = provider.GetRequiredService().HttpContext; + if (httpContext == null) + { + throw new InvalidOperationException(IDWebErrorMessage.HttpContextIsNull); + } + + return httpContext.Session; + }); return services; }