Search code examples
asp.net-coredeploymentrazor-pagesasp.net-authorization

ASP.NET Core Razor Pages lands on index page instead of LogIn page


I created a Razor Pages App using ASP.NET Core. My start page should be the Login page I created using ASP.NET Core Authentication. Everything was working as intended when I was building and testing the app on my computer. However, after I had published the application, the app starts on the index page instead of the login page.

This is my program.cs file:

using InvoicesApplication.Data;
using InvoicesApplication.Data.Repositories;
using InvoicesApplication.Services;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging.AzureAppServices;
using OfficeOpenXml;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

var builder = WebApplication.CreateBuilder(args);

ExcelPackage.LicenseContext = LicenseContext.NonCommercial; // Set the EPPlus license context

// Add services to the container.
// Dependency Injections

builder.Services.AddRazorPages();

builder.Services.AddDbContext<InvoicesAppContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("InvoicesApplication"))); // Set up DI for database connection

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<InvoicesAppContext>(); // add identity 

// Add authentication and authorization
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.LoginPath = "/Identity/Account/Login";
        options.AccessDeniedPath = "/Identity/Account/Login";
    });

builder.Services.AddAuthorization(options =>
{
    options.FallbackPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
});

// Add the hosted service to DI
builder.Services.AddHostedService<EmailReminderTaskHostedService>();

builder.Services.AddScoped<ICustomerRepository, CustomerRepository>(); // Set up DI for the data (customer repository data)
builder.Services.AddScoped<IItemRepository, ItemRepository>(); // Set up DI for the data (item repository)
builder.Services.AddTransient<IEmailSender, EmailSender>(); // transient means that every time we want to send an email a new instance of the email sender service is created. 

builder.Services.AddTransient<IEmailReminderTask, EmailReminderTask>();

var app = builder.Build();

// Seed the database and apply migrations
using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    try
    {
        var context = services.GetRequiredService<InvoicesAppContext>();
        context.Database.Migrate(); // Apply any pending migrations

        var userManager = services.GetRequiredService<UserManager<IdentityUser>>();
        SeedData.SeedUsersAsync(userManager).Wait(); // Seed users
    }
    catch (Exception ex)
    {
        var logger = services.GetRequiredService<ILogger<Program>>();
        logger.LogError(ex, "An error occurred while migrating the database.");
    }
}

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}
else
{
    app.UseDeveloperExceptionPage();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication(); // Ensure the authentication middleware is added
app.UseAuthorization(); // Ensure the authorization middleware is added

app.MapRazorPages();

app.Run();

public static class SeedData
{
    public static async Task SeedUsersAsync(UserManager<IdentityUser> userManager)
    {
        // Define the users to be added
        var users = new List<(string Email, string Password)>
        {
           //removed sensitive information
        };

        foreach (var user in users)
        {
            if (await userManager.FindByEmailAsync(user.Email) == null)
            {
                var identityUser = new IdentityUser
                {
                    UserName = user.Email,
                    Email = user.Email,
                    EmailConfirmed = true
                };

                var result = await userManager.CreateAsync(identityUser, user.Password);
                if (!result.Succeeded)
                {
                    throw new Exception($"Failed to create user {user.Email}: {string.Join(", ", result.Errors.Select(e => e.Description))}");
                }
            }
        }
    }
}

This is the top of my Index page:

@page
@using InvoicesApplication.Data.Models;
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

and the associated page model:

namespace InvoicesApplication.Pages
{
    [Authorize]
    public class IndexModel : PageModel
    {
        private ICustomerRepository _customerRepo;
        private IItemRepository _itemRepo;
        private IEmailSender _emailSender;
        private IEmailReminderTask _emailReminderTask;
        private ILogger<IndexModel> _logger; // register logger service

and these are the tops of my Login page and page model, respectively:

@page
@model LoginModel

@{
    ViewData["Title"] = "Log in";
}

<div>

    <div class="container-md align-content-center">

namespace InvoicesApplication.Areas.Identity.Pages.Account
{
    public class LoginModel : PageModel
    {
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly ILogger<LoginModel> _logger;

This is what I tried so far:

  • I applied the attribute [Authorize] to all pages and globally in Program.cs.
  • I configured Program.cs with the correct setup for authentication and authorization.
  • I added JS console logs to see whether Login page was loaded first and then the Index page second (Login page never loads).
  • I added users and their passwords as to have something in the database (I was just trying things).
  • I have tried Mike Brind's solution, which calls for making the Login page as the home page. I get an Ambiguous Match error (Index page and Login Page).
  • I tried to specify the routes in the templates, i.e. @page "/Index" and @page "/Identity/Account/Login" but again nothing.

If anyone could provide any insights on how to address this problem, I would greatly appreciate.

Thank you in advance.


Solution

  • The problem was this code in program.cs:

    // Add authentication and authorization
    builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddCookie(options =>
        {
            options.LoginPath = "/Identity/Pages/Account/Login";
            options.AccessDeniedPath = "/Identity/Pages/Account/AccessDenied";
        });
    
    builder.Services.AddAuthorization(options =>
    {
        options.FallbackPolicy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build();
    });
    

    When I removed it, everything was working as intended. I am not sure and how since I am new to ast.net core but since it's working then I am fine. If anyone could shed some light on it, then that's great.