I have a blazor wasm project with ASP.net core hosting. Authentication i.e login,register is located on the server project via razor pages (.cshtml). When a user has succesfully logged onto to app, I need to be able to change certain aspects of the UI on theses razor pages if the current user is logged in. In past asp.net core projects the usual process for this would be to utilize the HTTPContext e.g
@if (!HttpContext.User.Identity.IsAuthenticated)
{
<li><a class="log-in-cta" asp-area="identity" asp-page="/account/login">Log in</a></li>
}
else
{
<li><a class="log-in-cta" asp-page="/home">Log otut</a></li>
}
However this returns false regardless of whether the current user is logged in or not. I've had a look online and alot of people are saying HTTPcontext should not be used in a blazor app. My question is if this is the case what is the alternative to used for the razor pages on the server project of a blazor app?
Project Solution
Login page trying to utilize httpcontext
@page
@model LoginModel
@{
ViewData["Title"] = "Log in";
}
@if (HttpContext.User.Identity.IsAuthenticated)
{
<p>You are already logged in </p>
<a asp-page="./Logout">Logout</a>
}
else
{
<div class="lighthouse-bg">
<div class="container">
<div class="account-form-wrapper">
<a href="/"><img class="beacon-logo" src="/img/logos/beacon-assist-logo.png" alt="beacon assist logo"></a>
<div class="account-form">
<div class="account-form-titles">
<h1 class="account-form__title">Login</h1>
</div>
<!-- Login form-->
<form method="post" asp-route-returnurl="@ViewData["ReturnUrl"]">
<input asp-for="@Model.ReturnUrl" type="hidden" />
<!-- Form Group (email address)-->
<span asp-validation-for="Input.Email"></span>
<div class="account-form-input">
<div class="input-wrapper">
<div class="input-icon" id="email-icon">
<img src="/img/icons/icon-at.svg">
</div>
<input asp-for="Input.Email" id="email" placeholder="email address" type="email" autocomplete="email" tabindex="1" autofocus="" />
</div>
</div>
<!-- Form Group (password)-->
<span asp-validation-for="Input.Password"></span>
<div class="account-form-input">
<div class="input-wrapper">
<div class="input-icon" id="password-icon">
<img src="/img/icons/icon-key.svg">
</div>
<input asp-for="Input.Password" id="password" placeholder="password" type="password" autocomplete="existing-password" />
</div>
</div>
<!-- Form Group (remember password checkbox)-->
<div class="account-form-checkbox-wrapper">
<label class="account-form-checkbox">
Remember me ?
<input asp-for="Input.RememberMe" id="check" type="checkbox" />
<span class="checkmark"></span>
</label>
</div>
<!-- Form Group (login box)-->
<div class="account-form-footer">
<button class="account-form-btn" type="submit">Login</button>
<a class="footer-link" asp-page="./ForgotPassword" tabindex="4">Forgot Password?</a>
</div>
<div asp-validation-summary="ModelOnly" class="font-weight-bold text-danger"></div>
</form>
</div>
</div>
</div>
</div>
}
Startup.cs of server project incase i have missed any configuration needed
namespace Tapiit.Beacon.Server
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.Configure<DatabaseOptions>(o =>
{
o.ConnectionString = Configuration["ConnectionStrings:DefaultConnection"];
});
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddDefaultIdentity<User>(o =>
{
// TODO: this was removed for testing
// o.SignIn.RequireConfirmedAccount = true;
o.Password.RequiredLength = 8;
})
.AddRoles<Role>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<User, ApplicationDbContext>(o =>
{
// https://github.com/dotnet/AspNetCore.Docs/issues/17649
o.IdentityResources["openid"].UserClaims.Add("role");
o.ApiResources.Single().UserClaims.Add("role");
o.ApiResources.Single().UserClaims.Add("name");
})
.AddProfileService<IdentityProfileService>();
// Need to do this as it maps "role" to ClaimTypes.Role and causes issues
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");
services.AddAuthentication()
.AddIdentityServerJwt();
services.AddAuthorization(o => o.AddAppPolicies());
services
.AddControllersWithViews(o =>
{
o.ModelBinderProviders.Insert(0, new LocalDateTimeModelBinderProvider());
o.ModelBinderProviders.Insert(0, new LocalDateModelBinderProvider());
o.ModelBinderProviders.Insert(0, new LocalTimeModelBinderProvider());
o.Filters.Add(new StatusCodeActionFilter());
})
.AddJsonOptions(o =>
{
o.JsonSerializerOptions.Converters.Add(new LocalDateTimeConverter());
o.JsonSerializerOptions.Converters.Add(new NullableLocalDateTimeConverter());
});
services.AddRazorPages();
services.AddHttpClient();
services.AddQueueService(o =>
{
o.AzureServiceBusConnectionString = Configuration["ConnectionStrings:AzureServiceBus"];
o.QueueName = Configuration["ServiceBusQueueName"];
});
AddHelpers(services);
AddRepositories(services);
AddMediatR(services);
}
private void AddHelpers(IServiceCollection services)
{
services.AddScoped<UserManager<User>>();
services.AddScoped<ISignInManager<User>, ApplicationSignInManager>();
services.AddSingleton<IClock>(NodaTime.SystemClock.Instance);
services.AddSingleton<IDataCache<DataCacheTag>, DataCache<DataCacheTag, DataCacheEntryInfo>>();
services.AddScoped<ISupportTeamUserHelper, SupportTeamUserHelper>();
services.AddSingleton<IVesselUserHelper, VesselUserHelper>();
services.AddSingleton<IVesselSupportTeamHelper, VesselSupportTeamHelper>();
services.AddScoped<IUserClaimIds, UserClaimIds>();
services.AddScoped<IAccountHelper, AccountHelper>();
services.AddScoped<IUserHelper, UserHelper>();
services.AddScoped<IVesselHelper, VesselHelper>();
services.AddScoped<IOrganisationHelper, OrganisationHelper>();
services.AddScoped<ISupportTeamHelper, SupportTeamHelper>();
services.AddScoped<IManageHelper, ManageHelper>();
services.AddScoped<ICityBasedRuleHelper, CityBasedRuleHelper>();
services.AddScoped<IGeoFenceRuleHelper, GeoFenceRuleHelper>();
}
private void AddRepositories(IServiceCollection services)
{
services.AddScoped<ISupportTeamUserRepository, SupportTeamUserRepository>();
services.AddSingleton<IVesselUserRepository, VesselUserRepository>();
services.AddSingleton<IVesselSupportTeamRepository, VesselSupportTeamRepository>();
services.AddScoped<IUserRepository, UserRepository>();
services.AddScoped<IVesselRepository, VesselRepository>();
services.AddScoped<IOrganisationRepository, OrganisationRepository>();
services.AddScoped<ISupportTeamRepository, SupportTeamRepository>();
services.AddScoped<ICityBasedRuleRepository, CityBasedRuleRepository>();
services.AddScoped<IGeoFenceRuleRepository, GeoFenceRuleRepository>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapFallbackToFile("index.html");
});
}
private static void AddMediatR(IServiceCollection services)
{
services.AddValidatorsFromAssembly(typeof(DummyValidator).Assembly);
// Add PipelineBehaviours in order of execution:
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehaviour<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviour<,>));
services.AddMediatR(typeof(DummyRequestHandler).Assembly);
}
}
}