Blazor WebAssembly - Prevent Access for Unauthorized Users - blazor-webassembly

I've got a Blazor webassembly app. It's part of an ASP.NET core website which has Cookie Authentication. You can login by visiting /login which is implemented as a .net core razor page.
services.AddRazorPages()
//.AddMvcOptions(options => options.Filters.Add(new AuthorizeFilter()))
.AddRazorPagesOptions(options =>
{
options.Conventions.AllowAnonymousToPage("/Login");
options.Conventions.AuthorizeFolder("/");
options.Conventions.AuthorizePage("/Logout");
options.Conventions.AllowAnonymousToPage("/Login");
});
services.AddAuthentication(options =>
{
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie(options =>
{
options.LoginPath = "/Login";
// true by default
options.SlidingExpiration = true;
// 14 days by default
options.ExpireTimeSpan = TimeSpan.FromDays(20);
});
When the user isn't logged in, it's still possible to visit any route in the Blazor WebAssembly app. All the .net core Views are (correctly) inaccessible.
Where do I configure the WebAssembly part so that it's inaccessible to a non-authenticated user?
NOTE, I don't want to implement OIDC authentication in the WebAssembly part of the app (described in the Microsoft documentation) - or to add any of the login/logout logic to the WebAssembly part, just to make all the routes exposed by the WebAssembly app inaccessible for a non-authenticated user.

Related

Blazor without Duende Identity Server

I have a small Blazor WASM project that I recently migrated to .net 6. But now I tried to run the published project and the application warned me that I don't have a license for Duende Identity Server.
My question is:
Can I do without the Duende Identity Server?
In my application, I need user login and role assignment. I want to have users defined only for this application and I want to use the application database to store them.
My Program.cs looks like this:
var builder = WebApplication.CreateBuilder(args);
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();
var appDbConStr = builder.Configuration.GetConnectionString("AppDbConnection");
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(appDbConStr));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<AppDbContext>()
.AddClaimsPrincipalFactory<AppClaimsPrincipalFactory>();
builder.Services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, AppDbContext>(opt =>
{
opt.IdentityResources["openid"].UserClaims.Add(UserClaim.Role);
opt.ApiResources.Single().UserClaims.Add(UserClaim.Role);
opt.IdentityResources["openid"].UserClaims.Add(UserClaim.Avatar);
opt.ApiResources.Single().UserClaims.Add(UserClaim.Avatar);
opt.IdentityResources["openid"].UserClaims.Add(UserClaim.Nick);
opt.ApiResources.Single().UserClaims.Add(UserClaim.Nick);
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");
builder.Services.AddAuthentication().AddIdentityServerJwt();
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddTransient<IRazorRendererHelper, RazorRendererHelper>();
builder.Services.AddScoped<Vks.Server.Services.SerialGenerator>();
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.MapControllers();
app.MapFallbackToFile("index.html");
app.Run();
Thank you
Use default ASP.NET CORE Identity (do not add reference to Duende).
Must read article covers all features of ASP.NET CORE Identity
https://chsakell.com/2018/04/28/asp-net-core-identity-series-getting-started/
Remove Duende reference by deleting reference on Microsoft.AspNetCore.ApiAuthorization.IdentityServer (duende is sub referenced)
Add asp.net core identity as follow:
builder.Services
.AddIdentity<ApplicationUser<int>, ApplicationRole>(config =>
{
config.SignIn.RequireConfirmedEmail = false;
config.Lockout.AllowedForNewUsers = true;
config.Lockout.MaxFailedAccessAttempts = 3;
config.User.RequireUniqueEmail = true;
config.Password.RequiredLength = 8;
//...other opts//
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddUserManager<CustomUserManager>() // inherited from UserManager with overriden logic
.AddDefaultTokenProviders()
.AddTokenProvider<CustomAuthenticatorTokenProvider>(TokenOptions.DefaultAuthenticatorProvider) // inherited from AuthenticatorTokenProvider with overriden logic
.AddPasswordValidator<CustomPasswordValidator>(); // implements IPasswordValidator for additional password validation

Dual authentication with ASP.NET Core MVC

I have an ASP.NET Core MVC web application and I want to allow my users to authenticate using identity server 4 and Azure Active Directory. All my internal users (company users) need to use Azure AD and all external users should use identity server to signing.
I have setup authentication in startup class as follows:
services.AddAuthentication(options =>
{
options.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddOpenIdConnect("oidc", options =>
{
options.Authority = Configuration["Identity:WebApiUrl"];
options.ClientId = "xxxxxxx";
options.ClientSecret = "xxxxxxxxx";
options.ResponseType = "id_token";
options.Scope.Add("openid");
options.Scope.Add("profile");
options.GetClaimsFromUserInfoEndpoint = true;
options.ClaimActions.MapUniqueJsonKey("email", "email");
options.SaveTokens = true;
})
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"));
This will only allow internal users to signin using azure AD and external users gets following error.
Exception: Correlation failed.
Unknown location
Exception: An error was encountered while handling the remote login.
Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler.HandleRequestAsync()
How can I solve this issue and allow both external and internal users to login ?
Thank you.

AWS Cognito Signin returning Bad gateway error

My .Net Core application is published to an elastic beanstalk load balanced environment and I'm using the Cognito hosted UI for authentication but after entering correct login details I get a 502 error.
snippet from startup.cs
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.ResponseType = Configuration["Authentication:Cognito:ResponseType"];
options.MetadataAddress = Configuration["Authentication:Cognito:MetadataAddress"];
options.ClientId = Configuration["Authentication:Cognito:ClientId"];
options.SaveTokens = true;
options.ClientSecret = Configuration["Authentication:Cognito:Secret"];
options.Scope.Add(Configuration["Authentication:Cognito:Scope"]);
options.Events = new OpenIdConnectEvents()
{
OnRedirectToIdentityProviderForSignOut = OnRedirectToIdentityProviderForSignOut,
OnRedirectToIdentityProvider = (async context =>
{
context.ProtocolMessage.RedirectUri = context.ProtocolMessage.RedirectUri.Replace("http:", "https:");
await Task.FromResult(0);
})
};
});
When I inspect the network activity in the browser I'm seeing this...
...which suggests that cognito is redirecting to /signin-oidc but there's no authorisation so it redirects back to cognito which then redirects back, and this repeats until it eventually throws the 502 error.
When I'm testing locally I'm able to login okay which makes me think it's maybe some kind of loadbalancing issue??
I kept getting a Bad Gateway 502 until I discovered that my next.config.js had async rewrites enabled for the source/destination URLs in my clumsy attempt to fix a CORS issue. Removing that fixed my problem to get next-auth working with cognito. Then I had to fix the underlying CORS problem with server side policy settings.

Asp core doesn't enforce client certificate

I have an API app created using asp core. I'm trying to enforce use of client certificates as described here.
I did tell Kestrel to require certificates in Program.cs:
webBuilder.ConfigureKestrel(o =>
{
o.ConfigureHttpsDefaults(o => o.ClientCertificateMode = ClientCertificateMode.RequireCertificate);
});
And I did add event handler in Startup.cs:
services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options =>
{
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
}
};
});
When I debug the API running locally it still doesn't require any certificates. If I provide certificate anyway, the breakpoint in the event handler is never hit.

.Net Core 3.1 as a standalone Identity Server

I would like to use a .Net Core 3.1 web app to allow an app (e.g. iPhone or Web Javascript) to authenticate with a username and password and receive a Jason Web Token (JWT) that contains claims about the user... if it succeeds, then the JWT can be sent as a bearer token to an API that would decode and validate the JWT (the token would be asymmetric and use a public/private key pair) and retrieve any claims that are embedded... perhaps a bonus if the app could decode the JWT as well in order to retrieve any claims.
Any thoughts on if this approach is possible? And, if there are any discussions or examples of how this might be done, that would be terrific.
Take a look at the examples that IdentityServer4 is providing. This sample/quickstart includes the case you are describing. https://github.com/IdentityServer/IdentityServer4/tree/main/samples/Quickstarts/6_AspNetIdentity/src
The API needs to be a scope in the IdentityServer4 configuration. It has a connection with the authority (IdentityServer4):
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://localhost:5001";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};
});
The Client, in this example an MVC Client, needs to be a client in IdentityServer4. There are many types of GrantTypes. https://identityserver4.readthedocs.io/en/latest/topics/grant_types.html
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.Authority = "https://localhost:5001";
options.ClientId = "mvc";
options.ClientSecret = "secret";
options.ResponseType = "code";
options.Scope.Add("api1");
options.SaveTokens = true;
});
Hope this helps you out

Resources