Opgave: Authentification - using Cookies


I denne opgave tager vi udgangspunkt i eksemplet ItemsRazor ItemRazorV5.zip





Opgaven går ud på at, der skal implementeres en login-funktion. Login-funktionen vil benytte en "simpel" Cookie-baseret autentifikation uden tilknyttet database (dvs løsningen er ikke baseret på det mere advancerede ASP.Net Identity framework).


Step 1 (User)
Tilføj en class User der indeholder properties til UserName og Password.
Husk: der skal være både en default konstruktør (ctor) og en konstruktør til at initialisere properties (ctorp)


Step 2 (MockUsers)
Tilføj en class MockUsers til mappen MockData.
Klassen skal have en statisk liste users, initialiseret med et par User objekter, samt en statisk metode GetMockUsers( ), der returnere listen users.

 

Step 3 (UserService)
Opret en ny klasse UserService (i mappen Services). Klassen skal have en property Users af typen List<User>, en konstruktør der initialiserer Users med MockData ved at kalde GetUsers( ) fra MockUsers.

 

Step 4 (Startup.cs)
Registrer UserService i ConfigureServices ved at tilføje: services.AddSingleton<UserService, UserService>();



Step 5 (Startup.cs)

Der skal nu tilføjes konfiguration for den cookie-baserede autentification.
Tilføj følgende kode til ConfigureServices( ) metoden i Startup.cs:

services.Configure<CookiePolicyOptions>(options => {
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
}); services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(cookieOptions => {
cookieOptions.LoginPath = "/Login/LoginPage";
}); services.AddMvc().AddRazorPagesOptions(options => {
options.Conventions.AuthorizeFolder("/Item");
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

og følgende kode: app.UseAuthentication( ); mellem app.UseRouting(); og app.UseAuthorization(); i Configure( ) metoden


Husk: Relevante using statements (imports), fx using Microsoft.AspNetCore.Http; og using Microsoft.AspNetCore.Authentication.Cookies;

 


Step 6 (LogInPage - Razor Page)
Opret en ny mappe LogIn (under Pages) og tilføj en ny Razor Pages: LoginPage til mappen.

 

Step 7 (LogInPage.cshtml.cs)

  1. LogInPageModel skal have en static property der kan referere til en User der er logget ind:

    public static User LoggedInUser { get; set; } = null;

  2. Der skal desuden være en reference til UserService:

    private UserService _userService;


    Husk, at initialisere servicen i konstruktøren!

  3. Og der skal være 3 properties: UserName, Password og Message af typen string. UserName skal annoteres med [BindProperty] og Password med [BindProperty, DataType(DataType.Password)]

  4. Til sidst skal OnPost( ) implementeres. Metoden skal først hente listen af Users via _userService og tjekke om brugeren med (UserName, Password) findes i listen.
    Hvis bruger findes sættes LoggedInUser til den aktuelle user og der oprettes en ny liste af Claim-objekter der initialiseres med et nyt Claim indeholdende vores brugers UserName.
    Dernæst oprettes et ClaimsIdentity - objekt med vores nye liste af Claims og der laves et SignIn med denne ClaimsIdentity:

    public async Task<IActionResult> OnPost()
    {

    List<User> users = _userService.Users;
    foreach (User user in users)
    {

    if (UserName == user.UserName && Password == user.Password)
    {

    LoggedInUser = user;

    var claims = new List<Claim> { new Claim(ClaimTypes.Name, UserName) };

    var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
    await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity));
    return RedirectToPage("/Item/GetAllItems");

    }

    }

    Message = "Invalid attempt";
    return Page();
    }



Step 8 (LogInPage.cshtml)
Her tilføjes html-elementer så bruger kan indtaste UserName og Password mm ala:


<div class="container">
<div>
@Model.Message
</div>
<form method="post">
<div class="form-group">
<label asp-for="UserName" class="col-form-label "></label>
<div>
<input asp-for="UserName" />
</div>
</div>
<div class="form-group">
<label asp-for="Password" class="col-form-label"></label>
<div>
<input asp-for="Password" />
</div>
</div>
<div class="form-group">
<button class="btn btn-outline-light">Log in</button>
</div>
</form>
</div>


Step 9 (Afprøv)
Afprøv, at det ikke er muligt at navigere til de andre sider uden der bliver omdirigeret til Login siden og at der efter vellykket login kan navigeres rundt mellem siderne.


Step 10 (LogOutPage Razor pages)
Opret en ny page: LogOutPage. Siden skal kun have en funktion OnGet( ):

public async Task<IActionResult> OnGet()
{
LoginPageModel.LoggedInUser = null;

await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return RedirectToPage("/index");
}


Denne funktion skal kaldes når der skal logges af.

 

Step 11 (_Layout.cshtml)
Der skal nu tilføjes knapper i menuen til Login og Logout:

Udskift linie 27: <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse"> med: <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row justify-content-between">

Tilføj efter linie 44:

<ul class="navbar-nav">
@{
if (LogInPageModel.LoggedInUser == null)
{
<li class="nav-item">
<a class="nav-link" asp-area="" asp-page="/LogIn/LoginPage">Login</a>
</li>
}
else
{
<li class="nav-item nav-link mr-3">
User: @LogInPageModel.LoggedInUser.UserName
</li>

<li class="nav-item">
<a class="nav-link" asp-area="" asp-page="/LogIn/LogoutPage">Logout</a>
</li>
}
}
</ul>

 

Bemærk: Der skal tilføjes en using til LogIn, tilføj følgende som linie 1 i filen: @using ItemRazor.Pages.LogIn

 

Step 12 (Index.cshtml.cs)
Når applikationen startes kan der ligge gamle login-cookies. Derfor skal vi lige sikre os at den tidligere user er "signed out". Det gøres ved at tilføje følgende kode til OnGet( ) metoden i Index.cshtml.cs

public void OnGet()
{
if (LoginPageModel.LoggedInUser == null)
{
HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}
}

 

Step 12 (Afprøv)
Afprøv at implementeringen virker.


God fornøjelse!
Henrik Høltzer