Before going to the topic, let’s have a brief explanation of Authentication and Authorization.
Authentication: Grant access/permission for users to enter the application. It’s like giving access/permission for a person to enter a building.
Authorization: This comes after Authentication. Grant permission for users only to certain pages of the application. It’s like a person who has access/permission to enter a building which has 10 floors, can ONLY go to the 2nd or 4th floor.
JWT (JSON Web Token)
As it says, JWToken is a JSON format string value. JWToken is issued for each valid user (Authentication). Token is created only once during user login. User will use that token in all subsequent HTTP requests for Authorization until that user log out from the application.
JWToken Configuration in ASP.NET Core
We would not go into to each and every detail of JWToken
configuration. There are lot of articles which explain that. Configure JWT using Microsoft.AspNetCore
. Authentication.JwtBearer
and Microsoft.IdentityModel.Tokens
. This is done in Startup.cs ConfigurationServices()
method.
You can see in the below code, there are two parts in token configuration, services.AddAuthentication()
and AddJwtBearer()
.
services.AddAuthentication()
: This section is to configure the Authentication Scheme or Mechanism we are going to use. Here, we tell ASP.NET Core to use JWT Bearer Token Authentication. This is very important as this is going to be used in Configure()
method later.
AddJwtBearer()
: In this section, we configure the Token
with Secret Key, Expiration Date, Consumer, etc. Secret Key is to encrypt and decrypt the token. Same secret key should be used while creating the token which we will see in “Create Token” topic.
public void ConfigureServices(IServiceCollection services)
{
services.AddSession(options => {
options.IdleTimeout = TimeSpan.FromMinutes(60);
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
//Provide a secret key to Encrypt and Decrypt the Token
var SecretKey = Encoding.ASCII.GetBytes
("YourKey-2374-OFFKDI940NG7:56753253-tyuw-5769-0921-kfirox29zoxv");
//Configure JWT Token Authentication
services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(token =>
{
token.RequireHttpsMetadata = false;
token.SaveToken = true;
token.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
//Same Secret key will be used while creating the token
IssuerSigningKey = new SymmetricSecurityKey(SecretKey),
ValidateIssuer = true,
//Usually, this is your application base URL
ValidIssuer = "http://localhost:45092/",
ValidateAudience = true,
//Here, we are creating and using JWT within the same application.
//In this case, base URL is fine.
//If the JWT is created using a web service, then this would be the consumer URL.
ValidAudience = "http://localhost:45092/",
RequireExpirationTime = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
});
}
User Model Class
We need a model class for user to login. Create a model class for User with user id, password and other credentials. Create a class User.cs under “Models” folder.
public class User
{
public string USERID { get; set; }
public string PASSWORD { get; set; }
public string FIRST_NAME { get; set; }
public string LAST_NAME { get; set; }
public string EMAILID { get; set; }
public string PHONE { get; set; }
public string ACCESS_LEVEL { get; set; }
public string READ_ONLY { get; set; }
}
Create Token
Step 1
Let’s create a class TokenProvider.cs which would create/generate token for the user. Token is created only once and used in all subsequent request until user logoff. Under root folder of the solution, create a class TokenProvider.cs.
Step 2
Before creating the Token
, we need to get the UserID
from the login page and check if the user is present in our database. For demo purposes, list of users are hard coded values stored in a list. In the real world, this would be from database or some data source. Let’s add a property (UserList
) to TokenProvider.cs class. This property is our user data store which has few hardcoded values.
//Using hard coded collection list as Data Store for demo purposes
//In reality, User data comes from Database or other Data Source.
private List UserList = new List
{
new User { USERID = "jsmith@email.com", PASSWORD = "test",
EMAILID = "jsmith@email.com", FIRST_NAME = "John",
LAST_NAME = "Smith", PHONE = "356-735-2748",
ACCESS_LEVEL = "Director", READ_ONLY = "true" },
new User { USERID = "srob@email.com", PASSWORD = "test",
FIRST_NAME = "Steve", LAST_NAME = "Rob",
EMAILID = "srob@email.com", PHONE = "567-479-8537",
ACCESS_LEVEL = "Supervisor", READ_ONLY = "false" },
new User { USERID = "dwill@email.com", PASSWORD = "test",
FIRST_NAME = "DJ", LAST_NAME = "Will",
EMAILID = "dwill@email.com", PHONE = "599-306-6010",
ACCESS_LEVEL = "Analyst", READ_ONLY = "false" },
new User { USERID = "JBlack@email.com", PASSWORD = "test",
FIRST_NAME = "Joe", LAST_NAME = "Black",
EMAILID = "JBlack@email.com", PHONE = "764-460-8610",
ACCESS_LEVEL = "Analyst", READ_ONLY = "true" }
};
Step 3
We need to set user permission for the application in the token (Authorization). In the token, we need to tell what level of permission user can have. User permissions are created as Claims
. While creating token, we are going to set user permission in Claims
Object collection and assign it to Token
. These Claims
values will be used to grant permission/authorize the user in controllers. In MVC controllers action methods, we would be using “ACCESS_LEVEL
” and “READ_ONLY
” claims to set user permission. For demo purposes, user claims are hard coded. Here, you can connect to your database and get user permission.
Let’s add a method (GetUserClaims()
) to get user permission levels and build claims object collection in TokenProvider.cs class.
//Using hard coded values in claims collection list as Data Store for demo.
//In reality, User data comes from Database or other Data Source.
private IEnumerable GetUserClaims(User user)
{
IEnumerable claims = new Claim[]
{
new Claim(ClaimTypes.Name, user.FIRST_NAME + " " + user.LAST_NAME),
new Claim("USERID", user.USERID),
new Claim("EMAILID", user.EMAILID),
new Claim("PHONE", user.PHONE),
new Claim("ACCESS_LEVEL", user.ACCESS_LEVEL.ToUpper()),
new Claim("READ_ONLY", user.READ_ONLY.ToUpper())
};
return claims;
}
Step 4
Now it’s time to create the token for the user. First, get the user id from login page and check if the user is in the UserList
collection property declared above. If the user id is in the list, then we have a registered user. If not, then authentication fails. Do not issue the token.
Second, get the password from login page and check if the password matches with the password in the UserList
. If yes, then create a token for user. If not, authentication fails and token is not created/issued.
To create JWToken
, we would be using two namespaces, System.IdentityModel.Tokens.Jwt
and Microsoft.IdentityModel.Tokens
. Let’s create a token using JwtSecurityToken()
class (Here, I am not covering the details of token creation. There are lot of articles which explain JWT token creation). While creating token, user claims values are loaded within the token “claims
” property. We are calling the above function GetUserClaims()
which loads claims for the User. Token
is created in LoginUser()
method which takes UserID
and Password
as input.
Let’s create a function LoginUser()
which takes UserID
and Password
as input parameters in TokenProvider.cs.
public string LoginUser(string UserID, string Password)
{
//Get user details for the user who is trying to login
var user = UserList.SingleOrDefault(x => x.USERID == UserID);
//Authenticate User, Check if it’s a registered user in Database
if (user == null)
return null;
//If it's registered user, check user password stored in Database
//For demo, password is not hashed. Simple string comparison
//In real, password would be hashed and stored in DB. Before comparing, hash the password
if (Password == user.PASSWORD)
{
//Authentication successful, Issue Token with user credentials
//Provide the security key which was given in the JWToken configuration in Startup.cs
var key = Encoding.ASCII.GetBytes
("YourKey-2374-OFFKDI940NG7:56753253-tyuw-5769-0921-kfirox29zoxv");
//Generate Token for user
var JWToken = new JwtSecurityToken(
issuer: "http://localhost:45092/",
audience: "http://localhost:45092/",
claims: GetUserClaims(user),
notBefore: new DateTimeOffset(DateTime.Now).DateTime,
expires: new DateTimeOffset(DateTime.Now.AddDays(1)).DateTime,
//Using HS256 Algorithm to encrypt Token
signingCredentials: new SigningCredentials(new SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha256Signature)
);
var token = new JwtSecurityTokenHandler().WriteToken(JWToken);
return token;
}
else
{
return null;
}
}
Few points to consider...
While creating token, we need to provide the same security key which is configured in Startup.cs for JWToken
configuration.
var key = Encoding.ASCII.GetBytes
("YourKey-2374-OFFKDI940NG7:56753253-tyuw-5769-0921-kfirox29zoxv");
“issuer
” and “audience
” should be the same value which is configured in Startup.cs in ConfigureServices()
method.
Finally, the TokenProvider.cs class looks like this:
using LoginDemo.Models;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
namespace LoginDemo
{
public class TokenProvider
{
public string LoginUser(string UserID, string Password)
{
//Get user details for the user who is trying to login
var user = UserList.SingleOrDefault(x => x.USERID == UserID);
//Authenticate User, Check if it’s a registered user in Database
if (user == null)
return null;
//If it is registered user, check user password stored in Database
//For demo, password is not hashed. It is just a string comparision
//In reality, password would be hashed and stored in Database.
//Before comparing, hash the password again.
if (Password == user.PASSWORD)
{
//Authentication successful, Issue Token with user credentials
//Provide the security key which is given in
//Startup.cs ConfigureServices() method
var key = Encoding.ASCII.GetBytes
("YourKey-2374-OFFKDI940NG7:56753253-tyuw-5769-0921-kfirox29zoxv");
//Generate Token for user
var JWToken = new JwtSecurityToken(
issuer: "http://localhost:45092/",
audience: "http://localhost:45092/",
claims: GetUserClaims(user),
notBefore: new DateTimeOffset(DateTime.Now).DateTime,
expires: new DateTimeOffset(DateTime.Now.AddDays(1)).DateTime,
//Using HS256 Algorithm to encrypt Token
signingCredentials: new SigningCredentials
(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
);
var token = new JwtSecurityTokenHandler().WriteToken(JWToken);
return token;
}
else
{
return null;
}
}
//Using hard coded collection list as Data Store for demo.
//In reality, User details would come from Database.
private List UserList = new List
{
new User { USERID = "jsmith@email.com",
PASSWORD = "test", EMAILID = "jsmith@email.com",
FIRST_NAME = "John", LAST_NAME = "Smith",
PHONE = "356-735-2748", ACCESS_LEVEL = "Director",
READ_ONLY = "true" },
new User { USERID = "srob@email.com", PASSWORD = "test",
FIRST_NAME = "Steve", LAST_NAME = "Rob",
EMAILID = "srob@email.com", PHONE = "567-479-8537",
ACCESS_LEVEL = "Supervisor", READ_ONLY = "false" },
new User { USERID = "dwill@email.com", PASSWORD = "test",
FIRST_NAME = "DJ", LAST_NAME = "Will",
EMAILID = "dwill@email.com", PHONE = "599-306-6010",
ACCESS_LEVEL = "Analyst", READ_ONLY = "false" },
new User { USERID = "JBlack@email.com", PASSWORD = "test",
FIRST_NAME = "Joe", LAST_NAME = "Black",
EMAILID = "JBlack@email.com", PHONE = "764-460-8610",
ACCESS_LEVEL = "Analyst", READ_ONLY = "true" }
};
//Using hard coded collection list as Data Store for demo.
//In reality, User data comes from Database or other Data Source
private IEnumerable GetUserClaims(User user)
{
IEnumerable claims = new Claim[]
{
new Claim(ClaimTypes.Name, user.FIRST_NAME + " " + user.LAST_NAME),
new Claim("USERID", user.USERID),
new Claim("EMAILID", user.EMAILID),
new Claim("PHONE", user.PHONE),
new Claim("ACCESS_LEVEL", user.ACCESS_LEVEL.ToUpper()),
new Claim("READ_ONLY", user.READ_ONLY.ToUpper())
};
return claims;
}
}
}
Token Storage
Now that we have authenticated the user and issued the token for that user, we need to store this token somewhere until the user logs out from the application. This is required since the token needs to be passed in each and every subsequent HTTP request after successful login. As mentioned above, we are not going to use any client (browser) side cookies to store the token.
Rather, we would be storing the token in server side in a user SESSION
. Create a SESSION
variable and store the token in it. After successful login, for each subsequent request, we would get the token from the session
variable and insert into incoming HTTP Request.
We would be doing this in HomeController
action method below, gets the token from TokenProvider.cs, create a Session
object “JWToken
” and store the token.
In HomeController.cs, there is a “LoginUser
” action method. From Index.cshtml, user would input User ID and Password and submit the page to “LoginUser
” action method in the HomeController.cs. In ‘LoginUser
” controller action method, we will be adding the token to session object name “JWToken
”.
HttpContext.Session.SetString("JWToken", userToken);
Middleware
Here comes the crucial part of the whole implementation. This part is more of a concept and few lines of code. We are going to do two things here:
- Insert the token into HTTP Request
- Load user claims into HTTP Request
Let’s understand the concept first. Trying to keep it simple, please bear with me.
Authentication and Authorization is handled through HTTP Request, to do that:
- Token should be part of HTTP Request and it should come from HTTP Request Header.
ClaimsPrinciple
andClaimsIdentity
(HttpContext.User.Identity
) object is created from current HTTP Context.- User Claims are read from HTTP Request header and loaded into HTTP
Claims
Identity object - In other words, Authorization is done through incoming HTTP Request, NOT directly reading from the Token.
- By doing this, HTTP Request itself is Authorized for that user.
To achieve the above:
- We need to insert the
Token
(which is stored in user session variable “JWToken
”) into each incoming HTTP Request. - Read user claims values from
Token
and load it into HTTP ContextClaims
Principle object. - If token in not available in
session
variable “JWToken
”, then HTTP Request header “Authorization
” would be empty. In that case, Claims Principle for that user will not be set in HTTP Context. That would deny permission for the user.
The below picture gives an idea about how we are going to insert the Token to HTTP header and set the Claims Principle in HTTP Context.
Custom Middleware app.Use()
The main idea to have this custom middleware to insert the token into incoming HTTP Request. Now we have logged in user Token stored in Session
variable “JWToken
”, We need to insert that token into all subsequent incoming HTTP Request. For this, we are going to write a few lines of code into ASP.NET Core Middleware. This is nothing but HTTP pipeline. Custom Middleware is added in Startup.cs Configure()
method.
P.S.: Token
is created only once during user login.
Middleware app.UseAuthentication()
Now we need to validate the token and load the claims to HTTP Request
context. UseAuthentication()
does this job for us. Before HTTP Request hits the MVC controller, UseAuthentication()
does the following:
- Decrypting and Validating the Token using the secret key provided in
AddJwtBearer()
configuration (underConfigureServices()
method in Startup.cs) - Setting the
User
object in HTTP Request Context - Finally, read the
Claims
values fromToken
and load it toHttpContext.User.Identity
object
Custom Middleware Code
In Startup.cs, add the following code to Configure()
method. Add the below code after app.UseCookiePolicy()
. Here, the code execution sequence is important.
app.UseSession();
//Add JWToken to all incoming HTTP Request Header
app.Use(async (context, next) =>
{
var JWToken = context.Session.GetString("JWToken");
if (!string.IsNullOrEmpty(JWToken))
{
context.Request.Headers.Add("Authorization", "Bearer " + JWToken);
}
await next();
});
//Add JWToken Authentication service
app.UseAuthentication();
Let’s go through the code:
app.UseSession()
is configuration for usingSession
objects.- To write custom middleware, use
app.Use()
. - First, we need the
Token
before we insert it to HTTP Request. We have stored the token inSession
. Get theToken
fromsession
variable “JWToken
”.var JWToken = context.Session.GetString("JWToken");
- The next line checks if
Token
is available inSession
. If not, user is not Authenticated. So user permission is denied. - If the
Token
is present inSession
variable “JWToken
”, then we have Authenticated user. - Now, we need to add the Token to the HTTP
Request
(Remember, User Identity is created through HTTPRequest
). The below code adds the token to all incoming HTTPRequest
s.context.Request.Headers.Add("Authorization", "Bearer " + JWToken);
- Note, we are adding the Token to a “
Authorization
” header of the HTTPRequest
. Yes, it’s important to add the token to “Authorization
” header and the token should be concatenated with a keyword “Bearer
”. - The next line of code is
app.UseAuthentication()
.- This line of code will look for the Authentication mechanism configured in
ConfigureServices()
method. In ourConfigureService()
, we have used “AddJwtBearer
” configuration, which is part ofMicrosoft.AspNetCore.Authentication namespace
. - Inside
AddJWtBeared()
, we have our token configuration with secret key, expiration date, etc. - When HTTP
Request
comes in,app.UseAuthentication()
will look for “Authorization
” header in the HTTPRequest
. It will read the value stored in “Authorization
” header and pass it toMicrosoft.AspNetCore.Authentication
.Microsoft.AspNetCore.Authentication
will evaluate and validate the token as per the configuration we have set for the token. This includes decrypting the token using the secret key we have given in the configuration and reading the claims from the token and loading the claims toHttpContext.User.Identity
object. Here, HTTP Context itself is Authenticated and Authorized. - This complete execution is valid only for one HTTP
Request
(that particular incoming request). We have to do this for all subsequent HTTPRequest
. That’s the reason we store theToken
insession
variable and assign theToken
to HTTPRequest
“Authorization
” header for all subsequent incoming HTTPRequest
. All incoming HTTPRequest
and outgoing HTTPResponse
goes through the HTTP Pipeline in Startup.csConfigure()
method.
- This line of code will look for the Authentication mechanism configured in
Finally, Startup.cs Configure() method looks like this:
// This method gets called by the runtime.
// Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseCookiePolicy();
//Add User session
app.UseSession();
//Add JWToken to all incoming HTTP Request Header
app.Use(async (context, next) =>
{
var JWToken = context.Session.GetString("JWToken");
if (!string.IsNullOrEmpty(JWToken))
{
context.Request.Headers.Add("Authorization", "Bearer " + JWToken);
}
await next();
});
//Add JWToken Authentication service
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
Login Page (Index.cshtml)
Now let’s create a simple login page (Index.cshtml) with user id and password textbox. Add User.cs model to view page. Here you can see the IF
condition User.Identity.IsAuthenticated
which checks if the user is Authenticated or not. “User
” object is part of System.Security.Claims
which is set in HTTP Context by the middleware. If user is authenticated, we show the user name from the claims
identity name property. If not, then we ask the user to login.
@model LoginDemo.Models.User
@{
ViewData["Title"] = "Home Page";
}
<div style="padding-top:50px;"></div>
<div style="padding-top:50px;">
@if (User.Identity.IsAuthenticated)
{
<div class="row">
You are Logged in as
<span style="font-size:large;color:forestgreen;">
@User.Identity.Name</span>
</div>
<div class="row" style="padding-top:50px;">
@Html.ActionLink("Log Off", "Logoff",
"Home", null, new { @class = "btn btn-primary btn-lg rph-login-button" })
</div>
}
else
{
<div class="row">
<div class="col-lg-4 col-md-4 col-sm-4">
<div>
@using (Html.BeginForm("LoginUser", "Home",
FormMethod.Post, new { role = "form" }))
{
<div>
@Html.AntiForgeryToken()
<div>
<label>User ID</label><br />
</div>
<div>
@Html.TextBoxFor(m => m.USERID,
new {@class = "form-control txtbox"})
</div>
<div style="padding-top:20px;"></div>
<div>
<label>Password</label><br />
</div>
<div>
@Html.PasswordFor(m => m.USERID,
new {@class = "form-control txtbox"})
</div>
</div>
<div class="padding-left:35%;width:40%;">
<div class="padding-top:20px;">
<input class="btn btn-primary
btn-lg rph-login-button"
type="submit" value="Login"/>
</div>
</div>
}
</div>
</div>
<div class="col-lg-8 col-md-8 col-sm-8">
<div style="padding-top:50px;">
<div><b>Please login with any of the below User ID,
Password is span style="font-size:large;color:forestgreen;"
>test</span> for all Users</b></div>
<div style="padding-top:10px;">
<ui style="list-style: none;">
<li>jsmith@email.com - Director, Read Only - true</li>
<li>srob@email.com - Supervisor, Read Only - false</li>
<li>dwill@email.com - Analyst, Read Only - false</li>
<li>JBlack@email.com - Analyst, Read Only - true</li>
</ui>
</div>
</div>
</div>
</div>
}
</div>
Home Controller
Let’s add two Action
methods in HomeController.cs. One for Index
(Login) page and the other one to submit the login page.
public IActionResult Index()
{
return View();
}
public IActionResult LoginUser(User user)
{
TokenProvider _tokenProvider = new TokenProvider();
//Authenticate user
var userToken = _tokenProvider.LoginUser(user.USERID.Trim(), user.PASSWORD.Trim());
if (userToken != null)
{
//Save token in session object
HttpContext.Session.SetString("JWToken", userToken);
}
return Redirect("~/Home/Index");
}
Action
method LoginUser(User user)
takes the user id and password values from login page. The below line does the authentication by checking user id and password in data store.
var userToken = _tokenProvider.LoginUser(user.USERID.Trim(), user.PASSWORD.Trim());
Next lines check if there is a token issued by TokenProvider()
. If yes, then save the token in user Session
variable “JWToken
”.
if (userToken != null)
{
//Save token in session object
HttpContext.Session.SetString("JWToken", userToken);
}
Then, redirect the page to Index.cshtml:
return Redirect("~/Home/Index");
During the page redirection, we have already stored the token in session
object. Now the page redirection goes through the HTTP pipeline in Startup.cs. Now the custom middleware will stop the HTTP Request
and insert the token into HTTP Request
header “Authorization
”. Please refer to "Middleware" for more details.
If token
in not available in session
variable “JWToken
”, then HTTP Request
header “Authorization
” would be empty. In that case, HTTP Context
will not be set for that user. Redirection will ask the user to login.
Log Off
Let's log off the user. When there is no token, then HTTP Context cannot be set for the user. So, remove the token
from session
object. To remove the token
from session
, clear the session
for the user and redirect to another controller action.
Add a controller action method Logoff()
. Clear the session
for the user and redirect to Index
action method. It is important to redirect to another controller action method. Let's see why? Say, in Logoff()
action method, we return a View()
instead of Redirect()
. In this case, view page will be rendered to the browser and still users can access that page, User.Identity.IsAuthenticated
is still true
. When ASP.NET executes controller action method, it's in the process of HTTP RESPONSE
. Which means it had already passed through HTTP REQUEST
. User Claims Principle is set in HTTP Request
. By logging off the user, we need to clear the Claims Principle for that user as well. Clearing the session alone is not enough. So we need to go through the HTTP Pipeline again. Redirection to another controller goes through the HTTP Pipeline and it will look for the Token
in session
variable "JWToken
". But we have cleared the session
, token
is not in session
anymore. Without token, Claims Principle cannot be set in the HTTP Context. This will completely log out the user.
public IActionResult Logoff()
{
HttpContext.Session.Clear();
return Redirect("~/Home/Index");
}
Controller Code
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using LoginDemo.Models;
using System.Security.Claims;
using Microsoft.AspNetCore.Http;
namespace LoginDemo.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult LoginUser(User user)
{
TokenProvider _tokenProvider = new TokenProvider();
var userToken = _tokenProvider.LoginUser(user.USERID.Trim(),
user.PASSWORD.Trim());
if (userToken != null)
{
//Save token in session object
HttpContext.Session.SetString("JWToken", userToken);
}
return Redirect("~/Home/Index");
}
public IActionResult Logoff()
{
HttpContext.Session.Clear();
return Redirect("~/Home/Index");
}
}
}
Login Demo Project
Login Page
LoginDemo.sln
Part 2
In Part 2, we will cover Authorization for users. We are going to see:
- How to give page level access to users
- How to create custom authorize filter attribute to restrict users on controller level and action method level
- Decorate controller action methods with custom authorize attributes
- Restrict users from directly accessing a page without login
No comments:
Post a Comment