1

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

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);
        }
    }
}
  • 1
    https://stackoverflow.com/questions/60264657/get-current-user-in-a-blazor-component – abdusco Sep 16 '21 at 15:55
  • Yes I am aware they are different things. The project I am working on is a blazor app, however it still utilises razor pages on the server project for the purposes of identity e.g. login, register, forgot password. I implemented these via scaffolding, and need to change content on the login page based on whether or not the user is already logged in via httpcontext which always appears to be false, which is why I'm asking do I still use httpcontext for these particular razor pages as they are within a blazor app? – user3384410 Sep 17 '21 at 06:53
  • Ah cool, there must be some other reason as to why it is not working then, possibly my configuration in startup. Thanks for the clarification I was just unsure as to whether it should work in the same regard with it being a blazor project. – user3384410 Sep 17 '21 at 09:19
  • I worked out the issue, it was the ordering in my startup,.cs i placed use authentication and authorisation at the very top of configure services and it's now working as it should – user3384410 Sep 17 '21 at 09:33

1 Answers1

-1

I figured out what was the issue: the ordering in my ConfigureServices method of the Startup.cs.

I moved use authentication and authorization to the very top of this method and HttpContext is now working as it should.

Thanks all for the help

Peter Csala
  • 17,736
  • 16
  • 35
  • 75