Thursday 17 October 2019

Authentication and Authorization in .Net core razor pages with Policies.

Step 1: Update your "Startup.cs=>ConfigureServices" method with following code:
services.Configure<CookiePolicyOptions>(options =>
{
    options.CheckConsentNeeded = context => true;
    options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddAuthentication(
    CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.AccessDeniedPath = "/account/accessDenied";
        options.LoginPath = "/account/login";
    });

Step 2: Update "Startup.cs=>Configure" method with following code lines:
app.UseCookiePolicy();
app.UseAuthentication();

Step 3: Design Login page with following html code:
@page
@model LoginModel
@{
}
<form method="post" data-ajax="true" data-ajax-method="post" data-ajax-complete="completed">
    <h3 class="text-center">Login</h3>
    <hr />
    <div class="row form-group">
        <label class="col-md-4">User Name</label>
        <div class="col-md-4">
            <input type="text" asp-for="Email" />
        </div>
    </div>
    <div class="row form-group">
        <label class="col-md-4">Password</label>
        <div class="col-md-4">
            <input type="text" asp-for="Password" />
        </div>
    </div>
    <div class="row form-group">
        <label class="col-md-4"></label>
        <div class="col-md-4">
            <input type="submit" value="Submit" class="btn btn-primary" />
        </div>
    </div>
</form>


@if (!string.IsNullOrWhiteSpace(Model.Message))
{
    <div>
        <h3>@Model.Message</h3>
    </div>
}
@section Scripts{
    <script src="~/js/jquery.unobtrusive-ajax.js"></script>
    <script>
        function completed(data) {
            window.location.href = "/";
        }
    </script>
}
<style>
    form {
        max-width: 500px;
        margin: 50px auto 0 auto;
        padding: 20px;
        border: 1px solid grey;
    }
</style>

Step 4: Update code behind file "LoginModel.cs" file with following code:
  public class LoginModel : PageModel
    {
        [BindProperty]
        public string Email { get; set; }
        [BindProperty]
        public string Password { get; set;  }
        [BindProperty]
        public string Message { get; set;  }
        public void OnGet()
        {
        }

        public async Task<JsonResult> OnPostAsync()
        {
            var claims = new List<Claim>
                {
                    new Claim(ClaimTypes.Name,Email),
                    new Claim(ClaimTypes.Email,Email),
                    new Claim(ClaimTypes.Role,"admin")
                    };

            var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
            var authProperties = new AuthenticationProperties
            {
                IsPersistent = false,
                RedirectUri = this.Request.Host.Value
            };

            await HttpContext.SignInAsync(
               CookieAuthenticationDefaults.AuthenticationScheme,
               new ClaimsPrincipal(claimsIdentity),
               authProperties);

            return new JsonResult(new { success = true });
        }
    }

Note: Now you have successfully implemented the cookies based security in your .net core razor pages app. This authentication bypass the [Authorize] attribute after successfully login. However if you want to implement your custom attribute with additional checks then you have to implement policies. Adding Custom attribute like MVC in .net core razor pages is not possible. So for this purpose, .net core provides Policies. Continue with following steps to implement policy in your .net core razor pages application.

Step 5: Add new class named "SessionTimeoutRequirement.cs" and update as bellow code:
 public class SessionTimeoutRequirement : IAuthorizationRequirement
    {
        public SessionTimeoutRequirement(string usernamePattern)//Remove "usernamePattern" parameter if no need.
        {
            UsernamePattern = usernamePattern;
        }

        public string UsernamePattern { get; } //Remove this property if no need.
    }

Step 6: Add new class named "SessionTimeoutRequirementHandler.cs" and update with following code:
 public class SessionTimeoutRequirementHandler : AuthorizationHandler<SessionTimeoutRequirement>
    {
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SessionTimeoutRequirement requirement)
        {
// Write your custom logic to implement
            if (context.User.Identity.Name == requirement.UsernamePattern)
            {
                context.Succeed(requirement);
            }

            return Task.CompletedTask;
        }
    }

Step 7: Update your "Startup.cs=>ConfigureServices" method with following code:
services.AddAuthorization(options =>
{
    options.AddPolicy("RolePolicy", policy => policy.RequireClaim(ClaimTypes.Role, "Admin"));
    options.AddPolicy("SessionTimeoutPolicy", policy => policy.Requirements.Add(new SessionTimeoutRequirement("test")));
});

Step 8: Now specify the policy with authorize attribute as following code:
[Authorize(Policy = "SessionTimeoutPolicy")]
public class IndexModel : PageModel
{
    public void OnGet()
    {
    }
}

No comments:

Post a Comment