diff --git a/src/Components/WebAssembly/Authentication.Msal/src/Models/MsalProviderOptions.cs b/src/Components/WebAssembly/Authentication.Msal/src/Models/MsalProviderOptions.cs index 20af240d0502..8fa6b8b29249 100644 --- a/src/Components/WebAssembly/Authentication.Msal/src/Models/MsalProviderOptions.cs +++ b/src/Components/WebAssembly/Authentication.Msal/src/Models/MsalProviderOptions.cs @@ -24,7 +24,12 @@ public class MsalProviderOptions /// /// Gets or sets the msal.js cache options. /// - public MsalCacheOptions Cache { get; set; } + public MsalCacheOptions Cache { get; set; } = new MsalCacheOptions + { + // This matches the defaults in msal.js + CacheLocation = "sessionStorage", + StoreAuthStateInCookie = false + }; /// /// Gets or set the list of default access tokens scopes to provision during the sign-in flow. diff --git a/src/Components/WebAssembly/Authentication.Msal/src/MsalDefaultOptionsConfiguration.cs b/src/Components/WebAssembly/Authentication.Msal/src/MsalDefaultOptionsConfiguration.cs index b0dc7ffa0707..c98c6f871e82 100644 --- a/src/Components/WebAssembly/Authentication.Msal/src/MsalDefaultOptionsConfiguration.cs +++ b/src/Components/WebAssembly/Authentication.Msal/src/MsalDefaultOptionsConfiguration.cs @@ -40,11 +40,6 @@ public void Configure(RemoteAuthenticationOptions options) } options.ProviderOptions.Authentication.NavigateToLoginRequestUrl = false; - options.ProviderOptions.Cache = new MsalCacheOptions - { - CacheLocation = "localStorage", - StoreAuthStateInCookie = true - }; } public void PostConfigure(string name, RemoteAuthenticationOptions options) diff --git a/src/Components/WebAssembly/Authentication.Msal/src/MsalWebAssemblyServiceCollectionExtensions.cs b/src/Components/WebAssembly/Authentication.Msal/src/MsalWebAssemblyServiceCollectionExtensions.cs index 0071f2b758a3..2d395058fc38 100644 --- a/src/Components/WebAssembly/Authentication.Msal/src/MsalWebAssemblyServiceCollectionExtensions.cs +++ b/src/Components/WebAssembly/Authentication.Msal/src/MsalWebAssemblyServiceCollectionExtensions.cs @@ -23,15 +23,23 @@ public static class MsalWebAssemblyServiceCollectionExtensions /// The to configure the . /// The . public static IServiceCollection AddMsalAuthentication(this IServiceCollection services, Action> configure) + { + return AddMsalAuthentication(services, configure); + } + + /// + /// Adds authentication using msal.js to Blazor applications. + /// + /// The type of the remote authentication state. + /// The . + /// The to configure the . + /// The . + public static IServiceCollection AddMsalAuthentication(this IServiceCollection services, Action> configure) + where TRemoteAuthenticationState : RemoteAuthenticationState, new() { services.AddRemoteAuthentication(); services.TryAddEnumerable(ServiceDescriptor.Singleton>, MsalDefaultOptionsConfiguration>()); - if (configure != null) - { - services.Configure(configure); - } - return services; } } diff --git a/src/Components/WebAssembly/WebAssembly.Authentication/src/Interop/AuthenticationService.ts b/src/Components/WebAssembly/WebAssembly.Authentication/src/Interop/AuthenticationService.ts index a0f6a9b3002b..919ee1d5e092 100644 --- a/src/Components/WebAssembly/WebAssembly.Authentication/src/Interop/AuthenticationService.ts +++ b/src/Components/WebAssembly/WebAssembly.Authentication/src/Interop/AuthenticationService.ts @@ -253,15 +253,26 @@ class OidcAuthorizeService implements AuthorizeService { export class AuthenticationService { static _infrastructureKey = 'Microsoft.AspNetCore.Components.WebAssembly.Authentication'; - static _initialized = false; + static _initialized : Promise; static instance: OidcAuthorizeService; public static async init(settings: UserManagerSettings & AuthorizeServiceSettings) { + // Multiple initializations can start concurrently and we want to avoid that. + // In order to do so, we create an initialization promise and the first call to init + // tries to initialize the app and sets up a promise other calls can await on. if (!AuthenticationService._initialized) { - AuthenticationService._initialized = true; - const userManager = await this.createUserManager(settings); - AuthenticationService.instance = new OidcAuthorizeService(userManager); + this._initialized = new Promise(async (resolve, reject) => { + try { + const userManager = await this.createUserManager(settings); + AuthenticationService.instance = new OidcAuthorizeService(userManager); + resolve(); + } catch (e) { + reject(e); + } + }); } + + await this._initialized; } public static getUser() { diff --git a/src/Components/WebAssembly/WebAssembly.Authentication/src/Interop/package.json b/src/Components/WebAssembly/WebAssembly.Authentication/src/Interop/package.json index b0ae30371739..dd40f447edc6 100644 --- a/src/Components/WebAssembly/WebAssembly.Authentication/src/Interop/package.json +++ b/src/Components/WebAssembly/WebAssembly.Authentication/src/Interop/package.json @@ -1,4 +1,5 @@ { + "private": true, "scripts": { "build": "npm run build:release", "build:release": "webpack --mode production --env.production --env.configuration=Release", diff --git a/src/Components/WebAssembly/WebAssembly.Authentication/src/Options/OidcProviderOptions.cs b/src/Components/WebAssembly/WebAssembly.Authentication/src/Options/OidcProviderOptions.cs index 9c1c74933ca4..56859689ed01 100644 --- a/src/Components/WebAssembly/WebAssembly.Authentication/src/Options/OidcProviderOptions.cs +++ b/src/Components/WebAssembly/WebAssembly.Authentication/src/Options/OidcProviderOptions.cs @@ -16,6 +16,11 @@ public class OidcProviderOptions /// public string Authority { get; set; } + /// + /// Gets or sets the metadata url of the oidc provider. + /// + public string MetadataUrl { get; set; } + /// /// Gets or sets the client of the application. /// diff --git a/src/Components/WebAssembly/WebAssembly.Authentication/src/WebAssemblyAuthenticationServiceCollectionExtensions.cs b/src/Components/WebAssembly/WebAssembly.Authentication/src/WebAssemblyAuthenticationServiceCollectionExtensions.cs index e8c2e8931080..352016df5bbb 100644 --- a/src/Components/WebAssembly/WebAssembly.Authentication/src/WebAssemblyAuthenticationServiceCollectionExtensions.cs +++ b/src/Components/WebAssembly/WebAssembly.Authentication/src/WebAssemblyAuthenticationServiceCollectionExtensions.cs @@ -78,50 +78,83 @@ public static IServiceCollection AddRemoteAuthenticationThe where the services were registered. public static IServiceCollection AddOidcAuthentication(this IServiceCollection services, Action> configure) { - AddRemoteAuthentication(services, configure); + return AddOidcAuthentication(services, configure); + } + /// + /// Adds support for authentication for SPA applications using and the . + /// + /// The type of the remote authentication state. + /// The to add the services to. + /// An action that will configure the . + /// The where the services were registered. + public static IServiceCollection AddOidcAuthentication(this IServiceCollection services, Action> configure) + where TRemoteAuthenticationState : RemoteAuthenticationState, new() + { services.TryAddEnumerable(ServiceDescriptor.Singleton>, DefaultOidcOptionsConfiguration>()); - if (configure != null) - { - services.Configure(configure); - } - - return services; + return AddRemoteAuthentication(services, configure); } /// /// Adds support for authentication for SPA applications using and the . /// + /// The type of the remote authentication state. /// The to add the services to. /// The where the services were registered. public static IServiceCollection AddApiAuthorization(this IServiceCollection services) { - var inferredClientId = Assembly.GetCallingAssembly().GetName().Name; - - services.AddRemoteAuthentication(); - - services.TryAddEnumerable( - ServiceDescriptor.Singleton>, DefaultApiAuthorizationOptionsConfiguration>(_ => - new DefaultApiAuthorizationOptionsConfiguration(inferredClientId))); + return AddApiauthorizationCore(services, configure: null, Assembly.GetCallingAssembly().GetName().Name); + } - return services; + /// + /// Adds support for authentication for SPA applications using and the . + /// + /// The type of the remote authentication state. + /// The to add the services to. + /// The where the services were registered. + public static IServiceCollection AddApiAuthorization(this IServiceCollection services) + where TRemoteAuthenticationState : RemoteAuthenticationState, new() + { + return AddApiauthorizationCore(services, configure: null, Assembly.GetCallingAssembly().GetName().Name); } /// /// Adds support for authentication for SPA applications using and the . /// + /// The type of the remote authentication state. /// The to add the services to. /// An action that will configure the . /// The where the services were registered. public static IServiceCollection AddApiAuthorization(this IServiceCollection services, Action> configure) { - services.AddApiAuthorization(); + return AddApiauthorizationCore(services, configure, Assembly.GetCallingAssembly().GetName().Name); + } - if (configure != null) - { - services.Configure(configure); - } + /// + /// Adds support for authentication for SPA applications using and the . + /// + /// The type of the remote authentication state. + /// The to add the services to. + /// An action that will configure the . + /// The where the services were registered. + public static IServiceCollection AddApiAuthorization(this IServiceCollection services, Action> configure) + where TRemoteAuthenticationState : RemoteAuthenticationState, new() + { + return AddApiauthorizationCore(services, configure, Assembly.GetCallingAssembly().GetName().Name); + } + + private static IServiceCollection AddApiauthorizationCore( + IServiceCollection services, + Action> configure, + string inferredClientId) + where TRemoteAuthenticationState : RemoteAuthenticationState, new() + { + services.TryAddEnumerable( + ServiceDescriptor.Singleton>, DefaultApiAuthorizationOptionsConfiguration>(_ => + new DefaultApiAuthorizationOptionsConfiguration(inferredClientId))); + + services.AddRemoteAuthentication(configure); return services; } diff --git a/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Startup.cs b/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Startup.cs index 56e6cb43e0d3..2a8f10d22585 100644 --- a/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Startup.cs +++ b/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Startup.cs @@ -52,8 +52,8 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseRouting(); - app.UseAuthentication(); app.UseIdentityServer(); + app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => diff --git a/src/ProjectTemplates/ComponentsWebAssembly.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/App.razor b/src/ProjectTemplates/ComponentsWebAssembly.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/App.razor index 9e038086aedf..c491110ce2b4 100644 --- a/src/ProjectTemplates/ComponentsWebAssembly.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/App.razor +++ b/src/ProjectTemplates/ComponentsWebAssembly.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/App.razor @@ -15,7 +15,14 @@ - + @if(!context.User.Identity.IsAuthenticated) + { + + } + else + { +

You are not authorized to access this resource.

+ }
diff --git a/src/ProjectTemplates/ComponentsWebAssembly.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Shared/RedirectToLogin.razor b/src/ProjectTemplates/ComponentsWebAssembly.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Shared/RedirectToLogin.razor index 7fa0d95bafe2..b535ae4216f2 100644 --- a/src/ProjectTemplates/ComponentsWebAssembly.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Shared/RedirectToLogin.razor +++ b/src/ProjectTemplates/ComponentsWebAssembly.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Shared/RedirectToLogin.razor @@ -3,6 +3,6 @@ @code { protected override void OnInitialized() { - Navigation.NavigateTo($"authentication/login?returnUrl={Navigation.Uri}"); + Navigation.NavigateTo($"authentication/login?returnUrl={Uri.EscapeDataString(Navigation.Uri)}"); } } diff --git a/src/ProjectTemplates/ComponentsWebAssembly.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Startup.cs b/src/ProjectTemplates/ComponentsWebAssembly.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Startup.cs index 7359cb33d108..3dfd09e9c5b0 100644 --- a/src/ProjectTemplates/ComponentsWebAssembly.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Startup.cs +++ b/src/ProjectTemplates/ComponentsWebAssembly.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Startup.cs @@ -107,12 +107,12 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseRouting(); -#if (OrganizationalAuth || IndividualAuth) - app.UseAuthentication(); -#endif #if (IndividualLocalAuth) app.UseIdentityServer(); #endif +#if (OrganizationalAuth || IndividualAuth) + app.UseAuthentication(); +#endif #if (!NoAuth) app.UseAuthorization();