Implementing authorization

In the first part of the chapter, you saw how to handle user authentication and how to work with user logins. In the next part, you will see how to manage user access, which will allow you to fine-tune who has access to what.

The simplest authorization method is to use the [Authorize] meta decorator, which disables anonymous access completely. Users need to be signed in to be able to access restricted resources in this case.

Now, let's go and see how to implement it within the Tic-Tac-Toe application:

  1. Add a new method called SecuredPage to HomeController, and remove anonymous access to it by adding the [Authorize] decorator:
        [Authorize] 
        public async Task<IActionResult> SecuredPage() 
        { 
          return await Task.Run(() => 
          { 
            ViewBag.SecureWord = "Secured Page"; 
            return View("SecuredPage"); 
          }); 
        }  
  1. Add a new view called SecuredPage to the Views/Home folder:
        @{ 
          ViewData["Title"] = "Secured Page"; 
        } 
        @section Desktop {<h2>@Localizer["DesktopTitle"]</h2>} 
        @section Mobile {<h2>@Localizer["MobileTitle"]</h2>} 
        <div class="row"> 
          <div class="col-lg-12"> 
            <h2>Tic-Tac-Toe @ViewBag.SecureWord</h2> 
          </div> 
        </div> 
  1. Try accessing the secured page by entering its URL, http://<host>/Home/SecuredPage, manually while not signed in. You will be redirected automatically to the Login page:

  1. Enter valid user credentials and sign in. You should be automatically redirected to the secured page and now be able to view it:

Another relatively popular approach is to use role-based security, which provides some more advanced features. It is one of the recommended methods for securing your ASP.NET Core 3 web applications.

The following example explains how to work with it:

  1. Add a new class called UserRoleModel to the Models folder, and make it inherited from IdentityUserRole<long>. This will be used by the built-in ASP.NET Core 3 Identity authentication features:
        public class UserRoleModel : IdentityUserRole<Guid> 
        { 
          [Key] 
          public long Id { get; set; } 
        } 
  1. Update the OnModelCreating method within GameDbContext:
        protected override void OnModelCreating(ModelBuilder 
modelBuilder) { ... modelBuilder.Entity<IdentityUserRole<Guid>>() .ToTable("UserRoleModel") .HasKey(x => new { x.UserId, x.RoleId }); }
  1. Open the NuGet Package Manager Console and execute the Add-Migration IdentityDb2 command. Then, execute the Update-Database command.
  2. Update UserService, and modify the constructor to create two roles called Player and Administrator, if they do not yet exist:
        public UserService(RoleManager<RoleModel> roleManager,
ApplicationUserManager userManager, ILogger<UserService>
logger, SignInManager<UserModel> signInManager) { ... if (!roleManager.RoleExistsAsync("Player").Result) roleManager.CreateAsync(new RoleModel {
Name = "Player" }).Wait(); if (!roleManager.RoleExistsAsync("Administrator").Result)
roleManager.CreateAsync(new RoleModel {
Name = "Administrator" }).Wait(); }
  1. Update the RegisterUser method within UserService, and then add the user to the Player role or to the Administrator role during user registration:
... 
try 
{ 
  userModel.UserName = userModel.Email; 
  var result = await _userManager.CreateAsync
(userModel,userModel.Password); if (result == IdentityResult.Success) { if(userModel.FirstName == "Jason") await _userManager.AddToRoleAsync(userModel,"Administrator"); else await _userManager.AddToRoleAsync(userModel, "Player"); } return result == IdentityResult.Success; } ...
Note that in the example, the code to identify whether a user has the administrator role is intentionally very basic. You should implement something more sophisticated in your applications.
  1. Start the application and register a new user, and then open the RoleModel table within SQL Server Object Explorer and analyze its content:

  1. Open the UserRoleModel table within SQL Server Object Explorer and analyze its content:

  1. Update the SignInUser method within UserService to map roles with claims:
        ... 
        identity.AddClaim(new Claim("Score", 
user.Score.ToString())); var roles = await _userManager.GetRolesAsync(user); identity.AddClaims(roles?.Select(r => new
Claim(ClaimTypes.Role, r))); await httpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity),
new AuthenticationProperties { IsPersistent = false }); ...
  1. Update the SecuredPage method within HomeController, use the administrator role to secure access, and then replace the Authorize decorator that was there initially with the following:
        [Authorize(Roles = "Administrator")] 
  1. Start the application. If you try to access http://<host>/Home/SecuredPage without being logged in, you will be redirected to the Login page. Sign in as a user who has the player role, and you will be redirected to an Access Denied page (which does not exist, hence the 404 error) since the user does not have the administrator role:

  1. Log out and then sign in as a user who has the administrator role. You should now see the secured page, since the user has the necessary role:

In the following example, you will see how to sign in automatically as a registered user and how to activate claims-based and policy-based authentication:

  1. Update the SignInUser method, and then add a new method called SignIn to UserService:
public async Task<SignInResult> SignInUser(LoginModel loginModel, HttpContext httpContext) 
{ 
  ...
 
    await SignIn(httpContext, user); 
 
    return isValid; 
  } 
  catch (Exception ex) 
  { 
    ...
  } 
  finally 
  { 
    ...
  } 
} 

Implement the SignIn method as follows: 

private async Task SignIn(HttpContext httpContext, UserModel user) 
{ 
  var identity = new ClaimsIdentity(CookieAuthenticationDefaults.
AuthenticationScheme); identity.AddClaim(new Claim(ClaimTypes.Name, user.UserName)); identity.AddClaim(new Claim(ClaimTypes.GivenName,
user.FirstName)); identity.AddClaim(new Claim(ClaimTypes.Surname, user.LastName)); identity.AddClaim(new Claim("displayName", $"{user.FirstName}
{user.LastName}")); if (!string.IsNullOrEmpty(user.PhoneNumber)) identity.AddClaim(new Claim(ClaimTypes.HomePhone,
user.PhoneNumber)); identity.AddClaim(new Claim("Score", user.Score.ToString())); var roles = await _userManager.GetRolesAsync(user); identity.AddClaims(roles?.Select(r => new Claim(ClaimTypes.
Role, r))); if (user.FirstName == "Jason") identity.AddClaim(new Claim("AccessLevel", "Administrator")); await httpContext.SignInAsync(CookieAuthenticationDefaults.
AuthenticationScheme,
new ClaimsPrincipal(identity),new AuthenticationProperties {
IsPersistent = false }); }
Note that, in the example, the code to identify whether a user has administrator privileges is intentionally very basic. You should implement something more sophisticated in your applications.
  1. Update the RegisterUser method in UserService, add a new parameter to automatically sign in a user after registration, and then re-extract the user service interface:
        public async Task<bool> RegisterUser(UserModel userModel,
bool isOnline = false) { ... if (result == IdentityResult.Success) { ... if (isOnline) { HttpContext httpContext =
new HttpContextAccessor().HttpContext; await Signin(httpContext, userModel); } } ... }
  1. Update the Index method in UserRegistrationController to automatically sign in a newly registered user:
        ... 
        await _userService.RegisterUser(userModel, true); 
        ... 
  1. Update the ConfirmGameInvitation method in GameInvitationController to sign an invited user in automatically:
        ... 
        await _userService.RegisterUser(new UserModel 
        { 
          Email = gameInvitation.EmailTo, 
          EmailConfirmationDate = DateTime.Now, 
          EmailConfirmed = true, 
          FirstName = "", 
          LastName = "", 
          Password = "Azerty123!", 
          UserName = gameInvitation.EmailTo 
        }, true); 
        ... 
  1. Add a new policy called AdministratorAccessLevelPolicy to the Startup class, just after the MVC Middleware configuration:
        services.AddAuthorization(options => 
        { 
          options.AddPolicy("AdministratorAccessLevelPolicy",
policy => policy.RequireClaim("AccessLevel",
"Administrator")); });
  1. Update the SecuredPage method within HomeController, using Policy instead of Role to secure access, and then replace the Authorize decorator:
        [Authorize(Policy = "AdministratorAccessLevelPolicy")]
Note that it can be required to limit access to only one specific middleware since several kinds of authentication middleware can be used with ASP.NET Core 3 (cookie, bearer, and more) at the same time.

For this case, the Authorize decorator you have seen before allows you to define which middleware can authenticate a user.

Here is an example allowing cookies and a bearer token:

        [Authorize(AuthenticationSchemes = "Cookie,Bearer",
Policy = "AdministratorAccessLevelPolicy")]
  1. Start the application, register a new user with an Administrator access level, sign in, and then access http://<host>/Home/SecuredPage. Everything should be working as before.
Note that you might need to clear your cookies and log in again to create a new authentication token with the required claims.
  1. Try accessing the secured page as a user who does not have the required access level; as before, you should be redirected to http://<host>/Account/AccessDenied?ReturnUrl=%2FHome%2FSecuredPage:

  1. Log out and then sign in as a user who has the Administrator role. You should now see the secured page since the user has the necessary role.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.34.226