asp.net-coreauthenticationrazor-pagesasp.net-core-identity

How can ensure a section of code is executed right after Authentication in ASP.NET Core Razor Pages?


I am using ASP.NET Core 8 Identity I would like to execute some code that ensures that the current user who has just logged in has specified the time zone they wish to use. If not, I want to redirect them to a page that asks for their time zone. If they do not specify their time zone, they should not be authenticated to use the application.

At first, I thought of just executing this code in a Razor Page code behind method and redirecting to that page upon authentication, but that means that all a user has to do it just type in the correct URL, and they are free to use the app normally.

How can I execute these checks after authenticating a user, while withholding their authentication status until they complete this task of specifying their time zone?


Solution

  • I have implement this feature by using middleware, you can check the detailed steps below.

    1. Create a TimeZoneMiddleware.cs file and register it.

    using Microsoft.AspNetCore.Identity;
    using TimeZoneApp.Data;
    
    namespace TimeZoneApp
    {
        public class TimeZoneMiddleware
        {
            private readonly RequestDelegate _next;
    
            public TimeZoneMiddleware(RequestDelegate next)
            {
                _next = next;
            }
    
            public async Task InvokeAsync(HttpContext context, UserManager<ApplicationUser> userManager)
            {
                if (context.User.Identity.IsAuthenticated)
                {
                    if (!context.Request.Path.StartsWithSegments(new PathString("/TimeZoneApp")))
                    {
                        var user = await userManager.GetUserAsync(context.User);
                        if (user != null && string.IsNullOrEmpty(user.TimeZone))
                        {
                            context.Response.Redirect("/TimeZoneApp");
                            return;
                        }
                    }
                }
    
                await _next(context);
            }
        }
    }
    

    2. Register it

    ...
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    
    app.UseRouting();
    
    app.UseAuthentication();
    app.UseAuthorization();
    // add it here
    app.UseMiddleware<TimeZoneMiddleware>();
    
    app.MapRazorPages();
    
    app.Run();
    

    3. My test Page

    @page
    @model TimeZoneApp.Pages.SetTimeZoneModel
    
    <h2>Set Your Time Zone</h2>
    
    <form method="post">
        <label for="timezone">Time Zone:</label>
        <select asp-for="UserTimeZone" class="form-control">
            <option value="">Select a time zone</option>
            <option value="UTC-05:00">Eastern Time (US & Canada)</option>
            <option value="UTC-08:00">Pacific Time (US & Canada)</option>
            <!-- Add more time zones as needed -->
        </select>
        <button type="submit" class="btn btn-primary">Set Time Zone</button>
    </form>
    
    
    
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.RazorPages;
    using TimeZoneApp.Data;
    
    namespace TimeZoneApp.Pages
    {
        [Authorize]
        public class SetTimeZoneModel : PageModel
        {
            private readonly UserManager<ApplicationUser> _userManager;
    
            public SetTimeZoneModel(UserManager<ApplicationUser> userManager)
            {
                _userManager = userManager;
            }
    
            [BindProperty]
            public string UserTimeZone { get; set; }
    
            public async Task<IActionResult> OnPostAsync()
            {
                var user = await _userManager.GetUserAsync(User);
                if (user != null)
                {
                    user.TimeZone = UserTimeZone;
                    var result = await _userManager.UpdateAsync(user);
                    if (result.Succeeded)
                    {
                        return RedirectToPage("/Index");
                    }
                }
                return Page();
            }
        }
    }
    

    4. ApplicationUser.cs

    using Microsoft.AspNetCore.Identity;
    
    namespace TimeZoneApp.Data
    {
        public class ApplicationUser : IdentityUser
        {
            public string? TimeZone { get; set; }
        }
    
    }