[Blazor] Auth fixes (#20191)
* Adds MetadataAddress property to OidcProviderOptions. * Sets defaults for the Msal cache location on the provider initialization. * Updates the template to avoid redirecting to the login page if the user is already authenticated. * Fixes startup APIs for AddRemoteAuthentication. * Fixes TryGetToken for the Blazor MSAL library when the token can't be acquired silently.
This commit is contained in:
parent
3e07040bd1
commit
b0a95d05e0
|
|
@ -92,16 +92,12 @@ class MsalAuthorizeService implements AuthorizeService {
|
|||
scopes: scopes || this._settings.defaultAccessTokenScopes
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await this._msalApplication.acquireTokenSilent(tokenScopes);
|
||||
return {
|
||||
value: response.accessToken,
|
||||
grantedScopes: response.scopes,
|
||||
expires: response.expiresOn
|
||||
};
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
const response = await this._msalApplication.acquireTokenSilent(tokenScopes);
|
||||
return {
|
||||
value: response.accessToken,
|
||||
grantedScopes: response.scopes,
|
||||
expires: response.expiresOn
|
||||
};
|
||||
}
|
||||
|
||||
async signIn(state: any) {
|
||||
|
|
@ -236,7 +232,7 @@ class MsalAuthorizeService implements AuthorizeService {
|
|||
// msal.js doesn't support the state parameter on logout flows, which forces us to shim our own logout state.
|
||||
// The format then is different, as msal follows the pattern state=<<guid>>|<<user_state>> and our format
|
||||
// simple uses <<base64urlIdentifier>>.
|
||||
const appState = !isLogout? this._msalApplication.getAccountState(state[0]): state[0];
|
||||
const appState = !isLogout ? this._msalApplication.getAccountState(state[0]) : state[0];
|
||||
const stateKey = `${AuthenticationService._infrastructureKey}.AuthorizeService.${appState}`;
|
||||
const stateString = sessionStorage.getItem(stateKey);
|
||||
if (stateString) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "npm run build:release",
|
||||
"build:release": "webpack --mode production --env.production --env.configuration=Release",
|
||||
|
|
|
|||
|
|
@ -24,7 +24,12 @@ namespace Microsoft.Authentication.WebAssembly.Msal.Models
|
|||
/// <summary>
|
||||
/// Gets or sets the msal.js cache options.
|
||||
/// </summary>
|
||||
public MsalCacheOptions Cache { get; set; }
|
||||
public MsalCacheOptions Cache { get; set; } = new MsalCacheOptions
|
||||
{
|
||||
// This matches the defaults in msal.js
|
||||
CacheLocation = "sessionStorage",
|
||||
StoreAuthStateInCookie = false
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the list of default access tokens scopes to provision during the sign-in flow.
|
||||
|
|
|
|||
|
|
@ -40,11 +40,6 @@ namespace Microsoft.Authentication.WebAssembly.Msal
|
|||
}
|
||||
|
||||
options.ProviderOptions.Authentication.NavigateToLoginRequestUrl = false;
|
||||
options.ProviderOptions.Cache = new MsalCacheOptions
|
||||
{
|
||||
CacheLocation = "localStorage",
|
||||
StoreAuthStateInCookie = true
|
||||
};
|
||||
}
|
||||
|
||||
public void PostConfigure(string name, RemoteAuthenticationOptions<MsalProviderOptions> options)
|
||||
|
|
|
|||
|
|
@ -24,13 +24,21 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
/// <returns>The <see cref="IServiceCollection"/>.</returns>
|
||||
public static IServiceCollection AddMsalAuthentication(this IServiceCollection services, Action<RemoteAuthenticationOptions<MsalProviderOptions>> configure)
|
||||
{
|
||||
services.AddRemoteAuthentication<RemoteAuthenticationState, MsalProviderOptions>();
|
||||
services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<RemoteAuthenticationOptions<MsalProviderOptions>>, MsalDefaultOptionsConfiguration>());
|
||||
return AddMsalAuthentication<RemoteAuthenticationState>(services, configure);
|
||||
}
|
||||
|
||||
if (configure != null)
|
||||
{
|
||||
services.Configure(configure);
|
||||
}
|
||||
/// <summary>
|
||||
/// Adds authentication using msal.js to Blazor applications.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRemoteAuthenticationState">The type of the remote authentication state.</typeparam>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
|
||||
/// <param name="configure">The <see cref="Action{RemoteAuthenticationOptions{MsalProviderOptions}}"/> to configure the <see cref="RemoteAuthenticationOptions{MsalProviderOptions}"/>.</param>
|
||||
/// <returns>The <see cref="IServiceCollection"/>.</returns>
|
||||
public static IServiceCollection AddMsalAuthentication<TRemoteAuthenticationState>(this IServiceCollection services, Action<RemoteAuthenticationOptions<MsalProviderOptions>> configure)
|
||||
where TRemoteAuthenticationState : RemoteAuthenticationState, new()
|
||||
{
|
||||
services.AddRemoteAuthentication<RemoteAuthenticationState, MsalProviderOptions>(configure);
|
||||
services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<RemoteAuthenticationOptions<MsalProviderOptions>>, MsalDefaultOptionsConfiguration>());
|
||||
|
||||
return services;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -253,15 +253,21 @@ class OidcAuthorizeService implements AuthorizeService {
|
|||
export class AuthenticationService {
|
||||
|
||||
static _infrastructureKey = 'Microsoft.AspNetCore.Components.WebAssembly.Authentication';
|
||||
static _initialized = false;
|
||||
static _initialized : Promise<void>;
|
||||
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 = (async () => {
|
||||
const userManager = await this.createUserManager(settings);
|
||||
AuthenticationService.instance = new OidcAuthorizeService(userManager);
|
||||
})();
|
||||
}
|
||||
|
||||
await this._initialized;
|
||||
}
|
||||
|
||||
public static getUser() {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "npm run build:release",
|
||||
"build:release": "webpack --mode production --env.production --env.configuration=Release",
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Authentication
|
|||
/// </summary>
|
||||
public string Authority { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the metadata url of the oidc provider.
|
||||
/// </summary>
|
||||
public string MetadataUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the client of the application.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -18,11 +18,12 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Authentication
|
|||
{
|
||||
private string _message;
|
||||
private RemoteAuthenticationApplicationPathsOptions _applicationPaths;
|
||||
private string _action;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="RemoteAuthenticationActions"/> action the component needs to handle.
|
||||
/// </summary>
|
||||
[Parameter] public string Action { get; set; }
|
||||
[Parameter] public string Action { get => _action; set => _action = value?.ToLowerInvariant(); }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <typeparamref name="TAuthenticationState"/> instance to be preserved during the authentication operation.
|
||||
|
|
|
|||
|
|
@ -78,50 +78,83 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
/// <returns>The <see cref="IServiceCollection"/> where the services were registered.</returns>
|
||||
public static IServiceCollection AddOidcAuthentication(this IServiceCollection services, Action<RemoteAuthenticationOptions<OidcProviderOptions>> configure)
|
||||
{
|
||||
AddRemoteAuthentication<RemoteAuthenticationState, OidcProviderOptions>(services, configure);
|
||||
return AddOidcAuthentication<RemoteAuthenticationState>(services, configure);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds support for authentication for SPA applications using <see cref="OidcProviderOptions"/> and the <see cref="RemoteAuthenticationState"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRemoteAuthenticationState">The type of the remote authentication state.</typeparam>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
|
||||
/// <param name="configure">An action that will configure the <see cref="RemoteAuthenticationOptions{TProviderOptions}"/>.</param>
|
||||
/// <returns>The <see cref="IServiceCollection"/> where the services were registered.</returns>
|
||||
public static IServiceCollection AddOidcAuthentication<TRemoteAuthenticationState>(this IServiceCollection services, Action<RemoteAuthenticationOptions<OidcProviderOptions>> configure)
|
||||
where TRemoteAuthenticationState : RemoteAuthenticationState, new()
|
||||
{
|
||||
services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<RemoteAuthenticationOptions<OidcProviderOptions>>, DefaultOidcOptionsConfiguration>());
|
||||
|
||||
if (configure != null)
|
||||
{
|
||||
services.Configure(configure);
|
||||
}
|
||||
|
||||
return services;
|
||||
return AddRemoteAuthentication<TRemoteAuthenticationState, OidcProviderOptions>(services, configure);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds support for authentication for SPA applications using <see cref="ApiAuthorizationProviderOptions"/> and the <see cref="RemoteAuthenticationState"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRemoteAuthenticationState">The type of the remote authentication state.</typeparam>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
|
||||
/// <returns>The <see cref="IServiceCollection"/> where the services were registered.</returns>
|
||||
public static IServiceCollection AddApiAuthorization(this IServiceCollection services)
|
||||
{
|
||||
var inferredClientId = Assembly.GetCallingAssembly().GetName().Name;
|
||||
|
||||
services.AddRemoteAuthentication<RemoteAuthenticationState, ApiAuthorizationProviderOptions>();
|
||||
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IPostConfigureOptions<RemoteAuthenticationOptions<ApiAuthorizationProviderOptions>>, DefaultApiAuthorizationOptionsConfiguration>(_ =>
|
||||
new DefaultApiAuthorizationOptionsConfiguration(inferredClientId)));
|
||||
|
||||
return services;
|
||||
return AddApiauthorizationCore<RemoteAuthenticationState>(services, configure: null, Assembly.GetCallingAssembly().GetName().Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds support for authentication for SPA applications using <see cref="ApiAuthorizationProviderOptions"/> and the <see cref="RemoteAuthenticationState"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRemoteAuthenticationState">The type of the remote authentication state.</typeparam>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
|
||||
/// <returns>The <see cref="IServiceCollection"/> where the services were registered.</returns>
|
||||
public static IServiceCollection AddApiAuthorization<TRemoteAuthenticationState>(this IServiceCollection services)
|
||||
where TRemoteAuthenticationState : RemoteAuthenticationState, new()
|
||||
{
|
||||
return AddApiauthorizationCore<TRemoteAuthenticationState>(services, configure: null, Assembly.GetCallingAssembly().GetName().Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds support for authentication for SPA applications using <see cref="ApiAuthorizationProviderOptions"/> and the <see cref="RemoteAuthenticationState"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRemoteAuthenticationState">The type of the remote authentication state.</typeparam>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
|
||||
/// <param name="configure">An action that will configure the <see cref="RemoteAuthenticationOptions{ApiAuthorizationProviderOptions}"/>.</param>
|
||||
/// <returns>The <see cref="IServiceCollection"/> where the services were registered.</returns>
|
||||
public static IServiceCollection AddApiAuthorization(this IServiceCollection services, Action<RemoteAuthenticationOptions<ApiAuthorizationProviderOptions>> configure)
|
||||
{
|
||||
services.AddApiAuthorization();
|
||||
return AddApiauthorizationCore<RemoteAuthenticationState>(services, configure, Assembly.GetCallingAssembly().GetName().Name);
|
||||
}
|
||||
|
||||
if (configure != null)
|
||||
{
|
||||
services.Configure(configure);
|
||||
}
|
||||
/// <summary>
|
||||
/// Adds support for authentication for SPA applications using <see cref="ApiAuthorizationProviderOptions"/> and the <see cref="RemoteAuthenticationState"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRemoteAuthenticationState">The type of the remote authentication state.</typeparam>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
|
||||
/// <param name="configure">An action that will configure the <see cref="RemoteAuthenticationOptions{ApiAuthorizationProviderOptions}"/>.</param>
|
||||
/// <returns>The <see cref="IServiceCollection"/> where the services were registered.</returns>
|
||||
public static IServiceCollection AddApiAuthorization<TRemoteAuthenticationState>(this IServiceCollection services, Action<RemoteAuthenticationOptions<ApiAuthorizationProviderOptions>> configure)
|
||||
where TRemoteAuthenticationState : RemoteAuthenticationState, new()
|
||||
{
|
||||
return AddApiauthorizationCore<TRemoteAuthenticationState>(services, configure, Assembly.GetCallingAssembly().GetName().Name);
|
||||
}
|
||||
|
||||
private static IServiceCollection AddApiauthorizationCore<TRemoteAuthenticationState>(
|
||||
IServiceCollection services,
|
||||
Action<RemoteAuthenticationOptions<ApiAuthorizationProviderOptions>> configure,
|
||||
string inferredClientId)
|
||||
where TRemoteAuthenticationState : RemoteAuthenticationState, new()
|
||||
{
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IPostConfigureOptions<RemoteAuthenticationOptions<ApiAuthorizationProviderOptions>>, DefaultApiAuthorizationOptionsConfiguration>(_ =>
|
||||
new DefaultApiAuthorizationOptionsConfiguration(inferredClientId)));
|
||||
|
||||
services.AddRemoteAuthentication<TRemoteAuthenticationState, ApiAuthorizationProviderOptions>(configure);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,8 +52,8 @@ namespace Wasm.Authentication.Server
|
|||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseIdentityServer();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,14 @@
|
|||
<Found Context="routeData">
|
||||
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
|
||||
<NotAuthorized>
|
||||
<RedirectToLogin />
|
||||
@if (!context.User.Identity.IsAuthenticated)
|
||||
{
|
||||
<RedirectToLogin />
|
||||
}
|
||||
else
|
||||
{
|
||||
<p>You are not authorized to access this resource.</p>
|
||||
}
|
||||
</NotAuthorized>
|
||||
</AuthorizeRouteView>
|
||||
</Found>
|
||||
|
|
|
|||
|
|
@ -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)}");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,12 +107,12 @@ namespace ComponentsWebAssembly_CSharp.Server
|
|||
|
||||
app.UseRouting();
|
||||
|
||||
#if (OrganizationalAuth || IndividualAuth)
|
||||
app.UseAuthentication();
|
||||
#endif
|
||||
#if (IndividualLocalAuth)
|
||||
app.UseIdentityServer();
|
||||
#endif
|
||||
#if (OrganizationalAuth || IndividualAuth)
|
||||
app.UseAuthentication();
|
||||
#endif
|
||||
#if (!NoAuth)
|
||||
app.UseAuthorization();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue