[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
|
scopes: scopes || this._settings.defaultAccessTokenScopes
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
const response = await this._msalApplication.acquireTokenSilent(tokenScopes);
|
||||||
const response = await this._msalApplication.acquireTokenSilent(tokenScopes);
|
return {
|
||||||
return {
|
value: response.accessToken,
|
||||||
value: response.accessToken,
|
grantedScopes: response.scopes,
|
||||||
grantedScopes: response.scopes,
|
expires: response.expiresOn
|
||||||
expires: response.expiresOn
|
};
|
||||||
};
|
|
||||||
} catch (e) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async signIn(state: any) {
|
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.
|
// 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
|
// The format then is different, as msal follows the pattern state=<<guid>>|<<user_state>> and our format
|
||||||
// simple uses <<base64urlIdentifier>>.
|
// 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 stateKey = `${AuthenticationService._infrastructureKey}.AuthorizeService.${appState}`;
|
||||||
const stateString = sessionStorage.getItem(stateKey);
|
const stateString = sessionStorage.getItem(stateKey);
|
||||||
if (stateString) {
|
if (stateString) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run build:release",
|
"build": "npm run build:release",
|
||||||
"build:release": "webpack --mode production --env.production --env.configuration=Release",
|
"build:release": "webpack --mode production --env.production --env.configuration=Release",
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,12 @@ namespace Microsoft.Authentication.WebAssembly.Msal.Models
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the msal.js cache options.
|
/// Gets or sets the msal.js cache options.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// Gets or set the list of default access tokens scopes to provision during the sign-in flow.
|
/// 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.Authentication.NavigateToLoginRequestUrl = false;
|
||||||
options.ProviderOptions.Cache = new MsalCacheOptions
|
|
||||||
{
|
|
||||||
CacheLocation = "localStorage",
|
|
||||||
StoreAuthStateInCookie = true
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PostConfigure(string name, RemoteAuthenticationOptions<MsalProviderOptions> options)
|
public void PostConfigure(string name, RemoteAuthenticationOptions<MsalProviderOptions> options)
|
||||||
|
|
|
||||||
|
|
@ -24,13 +24,21 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
/// <returns>The <see cref="IServiceCollection"/>.</returns>
|
/// <returns>The <see cref="IServiceCollection"/>.</returns>
|
||||||
public static IServiceCollection AddMsalAuthentication(this IServiceCollection services, Action<RemoteAuthenticationOptions<MsalProviderOptions>> configure)
|
public static IServiceCollection AddMsalAuthentication(this IServiceCollection services, Action<RemoteAuthenticationOptions<MsalProviderOptions>> configure)
|
||||||
{
|
{
|
||||||
services.AddRemoteAuthentication<RemoteAuthenticationState, MsalProviderOptions>();
|
return AddMsalAuthentication<RemoteAuthenticationState>(services, configure);
|
||||||
services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<RemoteAuthenticationOptions<MsalProviderOptions>>, MsalDefaultOptionsConfiguration>());
|
}
|
||||||
|
|
||||||
if (configure != null)
|
/// <summary>
|
||||||
{
|
/// Adds authentication using msal.js to Blazor applications.
|
||||||
services.Configure(configure);
|
/// </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;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -253,15 +253,21 @@ class OidcAuthorizeService implements AuthorizeService {
|
||||||
export class AuthenticationService {
|
export class AuthenticationService {
|
||||||
|
|
||||||
static _infrastructureKey = 'Microsoft.AspNetCore.Components.WebAssembly.Authentication';
|
static _infrastructureKey = 'Microsoft.AspNetCore.Components.WebAssembly.Authentication';
|
||||||
static _initialized = false;
|
static _initialized : Promise<void>;
|
||||||
static instance: OidcAuthorizeService;
|
static instance: OidcAuthorizeService;
|
||||||
|
|
||||||
public static async init(settings: UserManagerSettings & AuthorizeServiceSettings) {
|
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) {
|
if (!AuthenticationService._initialized) {
|
||||||
AuthenticationService._initialized = true;
|
this._initialized = (async () => {
|
||||||
const userManager = await this.createUserManager(settings);
|
const userManager = await this.createUserManager(settings);
|
||||||
AuthenticationService.instance = new OidcAuthorizeService(userManager);
|
AuthenticationService.instance = new OidcAuthorizeService(userManager);
|
||||||
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this._initialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getUser() {
|
public static getUser() {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run build:release",
|
"build": "npm run build:release",
|
||||||
"build:release": "webpack --mode production --env.production --env.configuration=Release",
|
"build:release": "webpack --mode production --env.production --env.configuration=Release",
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,11 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Authentication
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Authority { get; set; }
|
public string Authority { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the metadata url of the oidc provider.
|
||||||
|
/// </summary>
|
||||||
|
public string MetadataUrl { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the client of the application.
|
/// Gets or sets the client of the application.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,12 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Authentication
|
||||||
{
|
{
|
||||||
private string _message;
|
private string _message;
|
||||||
private RemoteAuthenticationApplicationPathsOptions _applicationPaths;
|
private RemoteAuthenticationApplicationPathsOptions _applicationPaths;
|
||||||
|
private string _action;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the <see cref="RemoteAuthenticationActions"/> action the component needs to handle.
|
/// Gets or sets the <see cref="RemoteAuthenticationActions"/> action the component needs to handle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter] public string Action { get; set; }
|
[Parameter] public string Action { get => _action; set => _action = value?.ToLowerInvariant(); }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the <typeparamref name="TAuthenticationState"/> instance to be preserved during the authentication operation.
|
/// 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>
|
/// <returns>The <see cref="IServiceCollection"/> where the services were registered.</returns>
|
||||||
public static IServiceCollection AddOidcAuthentication(this IServiceCollection services, Action<RemoteAuthenticationOptions<OidcProviderOptions>> configure)
|
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>());
|
services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<RemoteAuthenticationOptions<OidcProviderOptions>>, DefaultOidcOptionsConfiguration>());
|
||||||
|
|
||||||
if (configure != null)
|
return AddRemoteAuthentication<TRemoteAuthenticationState, OidcProviderOptions>(services, configure);
|
||||||
{
|
|
||||||
services.Configure(configure);
|
|
||||||
}
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds support for authentication for SPA applications using <see cref="ApiAuthorizationProviderOptions"/> and the <see cref="RemoteAuthenticationState"/>.
|
/// Adds support for authentication for SPA applications using <see cref="ApiAuthorizationProviderOptions"/> and the <see cref="RemoteAuthenticationState"/>.
|
||||||
/// </summary>
|
/// </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="services">The <see cref="IServiceCollection"/> to add the services to.</param>
|
||||||
/// <returns>The <see cref="IServiceCollection"/> where the services were registered.</returns>
|
/// <returns>The <see cref="IServiceCollection"/> where the services were registered.</returns>
|
||||||
public static IServiceCollection AddApiAuthorization(this IServiceCollection services)
|
public static IServiceCollection AddApiAuthorization(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
var inferredClientId = Assembly.GetCallingAssembly().GetName().Name;
|
return AddApiauthorizationCore<RemoteAuthenticationState>(services, configure: null, Assembly.GetCallingAssembly().GetName().Name);
|
||||||
|
|
||||||
services.AddRemoteAuthentication<RemoteAuthenticationState, ApiAuthorizationProviderOptions>();
|
|
||||||
|
|
||||||
services.TryAddEnumerable(
|
|
||||||
ServiceDescriptor.Singleton<IPostConfigureOptions<RemoteAuthenticationOptions<ApiAuthorizationProviderOptions>>, DefaultApiAuthorizationOptionsConfiguration>(_ =>
|
|
||||||
new DefaultApiAuthorizationOptionsConfiguration(inferredClientId)));
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds support for authentication for SPA applications using <see cref="ApiAuthorizationProviderOptions"/> and the <see cref="RemoteAuthenticationState"/>.
|
/// Adds support for authentication for SPA applications using <see cref="ApiAuthorizationProviderOptions"/> and the <see cref="RemoteAuthenticationState"/>.
|
||||||
/// </summary>
|
/// </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="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>
|
/// <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>
|
/// <returns>The <see cref="IServiceCollection"/> where the services were registered.</returns>
|
||||||
public static IServiceCollection AddApiAuthorization(this IServiceCollection services, Action<RemoteAuthenticationOptions<ApiAuthorizationProviderOptions>> configure)
|
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)
|
/// <summary>
|
||||||
{
|
/// Adds support for authentication for SPA applications using <see cref="ApiAuthorizationProviderOptions"/> and the <see cref="RemoteAuthenticationState"/>.
|
||||||
services.Configure(configure);
|
/// </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;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,8 @@ namespace Wasm.Authentication.Server
|
||||||
|
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
|
||||||
app.UseAuthentication();
|
|
||||||
app.UseIdentityServer();
|
app.UseIdentityServer();
|
||||||
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
app.UseEndpoints(endpoints =>
|
app.UseEndpoints(endpoints =>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,14 @@
|
||||||
<Found Context="routeData">
|
<Found Context="routeData">
|
||||||
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
|
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
|
||||||
<NotAuthorized>
|
<NotAuthorized>
|
||||||
<RedirectToLogin />
|
@if (!context.User.Identity.IsAuthenticated)
|
||||||
|
{
|
||||||
|
<RedirectToLogin />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<p>You are not authorized to access this resource.</p>
|
||||||
|
}
|
||||||
</NotAuthorized>
|
</NotAuthorized>
|
||||||
</AuthorizeRouteView>
|
</AuthorizeRouteView>
|
||||||
</Found>
|
</Found>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,6 @@
|
||||||
@code {
|
@code {
|
||||||
protected override void OnInitialized()
|
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();
|
app.UseRouting();
|
||||||
|
|
||||||
#if (OrganizationalAuth || IndividualAuth)
|
|
||||||
app.UseAuthentication();
|
|
||||||
#endif
|
|
||||||
#if (IndividualLocalAuth)
|
#if (IndividualLocalAuth)
|
||||||
app.UseIdentityServer();
|
app.UseIdentityServer();
|
||||||
#endif
|
#endif
|
||||||
|
#if (OrganizationalAuth || IndividualAuth)
|
||||||
|
app.UseAuthentication();
|
||||||
|
#endif
|
||||||
#if (!NoAuth)
|
#if (!NoAuth)
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue