The new version for this was in progress.
Overview
In previous post (Basic authentication with OWIN), we know how the authentication can be integrated into TinyERP.
In basic authentication, it was not secure as we need to send username/ password along in header of each request sent to server. Someone can catch and get this credential information.
In this article, we will discuss about another more security authorization. it was called "Token Based Authentication".
diagram below describing how does it work:
We can see that, to get the list of protected resource(s), such as list of permissions:
- Client will send credential (user name/ password) to server and asks for token.
- Server will authorize base on this user name/ password. if success, new token will be sent back to client.
- Client continues sending new request to get expected resource(s) with new token attached in header.
- Server check and recognize attached token and return expected data.
This is execution flow of token bases authentication. Client only asks for token at the first time. This token can be re-used in sub-sequence request. So no credential was attached in those request also.
How to use token based authentication in TinyERP
Now, let try to figure out how to use token bases authorization to get the list of permissions.
The sample code for this article is available on my github (https://github.com/techcoaching/TinyERP in develop branch please).
Let start the app from App.Api project:
Use rest client tool and send request to "<host url>/api/permissions" and receive unauthorized response from server:
Why do I receive this, Look at the code of PermissionsController in App.Api, we have:
namespace App.Api.Features.Security
{
[RoutePrefix("api/permissions")]
public class PermissionsController : BaseApiController
{
[Authorize()]
[HttpGet]
[Route("")]
[ResponseWrapper()]
public IList<PermissionAsKeyNamePair> GetPermissions()
{
IPermissionService permissionService = IoC.Container.Resolve<IPermissionService>();
IList<PermissionAsKeyNamePair> pers = permissionService.GetPermissions();
return pers;
}
}
}
From the code, we see that "GetPermissions" method was marked with Authorize attribute. It means that this method only serves authorized request.
Ok, now let try to send request to "<host>/auth/token' with:
- Url: http://locahost:22383/auth/token
- Http Verb: POST
- Content-Type:application/x-www-form-urlencoded
- Content/ Body: grant_type=password&username=<username>&password=<password> (default login account:techcoaching/123).
this url (auth/token) was configured in "App.Common/Configurations/configuration.debug.config":
<authentication path="/auth/token" ... ></authentication>
So the request to server will be:
And response form server:
In the response, we have access_token, this is authorized token that we will use for sub-sequence request to server.
Now, let try to request to "<host url>/api/permissions" to get the list of permissions again, with detail as below:
- Url: http://localhost:22383/api/permissions
- Http Verb: GET
- Authorization (request header): bearer y7ewSZTydWxEdxZ7s-i8ScG3nID4d9tt_W5xJ0-7Bdw1Y3I5AbU7i7K7H_nk978XMQtvWCmZ6W9Uk4eF4EXjDOE0i5ty8cwyhuS-t6xYmEpVUh_ixvQ18vX7lnqfWrUzAL9ynRaGWfzLe8VYBqC3XFOYU_lpa00hEHI6xT2Np2RBNhOkL0_kpp9wWb4OMVfJ4dX9H48U7TX0o2Ltz5eP7wiOrwx9vgQP-BNk7O1ZDi3jQAtoiFoKGMPo1za3BGDY0dqjq5vctSR3XraEAs5cSGcsAuT28CEeFBKjDAtjJ2Mi_ZKi16TnqKYYSJnCzkAcUm7vIdbsz_txU3J_UTgewwO8MiBXU5owRhYbd-mr4sEaKbSOf9pu5U8ZXlQF39dO5_NJMpC7wsCqsq4JPJEaf8HR9Tfe7LMqOVMiCgCMf3Y (in format: <token_type> <access_token>)
The request will be:
and the response:
Yes, we can get the list of permissions (protected resources) now.
Role based authentication
In some case, we only want user with specified roles to access to specified resource. For example, in this case, we want only user with Administrator role can see the list of permissions.
Open PermissionsController and change Authorize attributefrom:
[Authorize()]
to:
[Authorize(Roles = SecurityRoleType.Administrator)]
So the final code will be:
[Authorize(Roles = SecurityRoleType.Administrator)]
[HttpGet]
[Route("")]
[ResponseWrapper()]
public IList<PermissionAsKeyNamePair> GetPermissions()
{
....
}
Let try to run the app and login again with user does not have administrator role. We will receive unauthorized response from server. Otherwise, the list of permission will be returned normally.
If you want more than 1 roles can access to this resources, just specify the list of roles separated by comma:
[Authorize(Roles = SecurityRoleType.Administrator + "," + SecurityRoleType.SuperUser)]
If you want to restrict all access to PermissionsController to authorized user, set Authorize attribute above class declaration:
namespace App.Api.Features.Security
{
[Authorize()]
[RoutePrefix("api/permissions")]
public class PermissionsController : BaseApiController
{
/*Body of class*/
}
}
How was Authorization implemented?
Currently, we have 2 ways for authorization, they are basic authorization and token based authorization.
Open "App.Common/Configurations/configuration.debug.config", we see the setting as:
<authentication authType="OwinTokenBase" ...></authentication>
This specify which authorization method we want to use. Valid values are OwinBasic or OwinTokenBase.
In this article, we will set it to "OwinTokenBase".
All the logic for security was located in "Security" solution folder:
"App.Security.Owin/ConfigAuthTask.cs" is where we config the authentication method. In this case, we only mention about token based authentication.
The main logic of OwinTokenAuthorizationServerProvider was located in Authorise method:
private AuthenticationResult Authorise(string userName, string password)
{
ICommandHandlerStrategy commandHandlerStrategy = CommandHandlerStrategyFactory.Create<User>();
UserNameAndPwdAuthenticationRequest request = new UserNameAndPwdAuthenticationRequest(userName, password);
commandHandlerStrategy.Execute(request);
return request.Result;
}
In this method, we just simply send authentication request and get back the result.
And where will handle this request, In "App.Security.Command.Impl/UserNameAndPwdAuthCommandHandler.cs", we have Handle function with receive "UserNameAndPwdAuthenticationRequest" class as parameter. this is where we will check the user name and password base on credential stored in database.
public void Handle(UserNameAndPwdAuthenticationRequest command)
{
this.ValidateUserNameAndPwdAuthenticationRequest(command);
using (IUnitOfWork uow = this.CreateUnitOfWork<User>())
{
IUserRepository repository = IoC.Container.Resolve<IUserRepository>(uow);
User user = repository.GetActiveUser(command.UserName, EncodeHelper.EncodePassword(command.Password));
if (user == null)
{
command.Result = new AuthenticationResult(false);
this.Publish(new OnAuthenticationFailed(command.UserName, command.Password, DateTime.UtcNow));
return;
}
user.GenerateLoginToken();
repository.Update(user);
uow.Commit();
command.Result = ObjectHelper.Convert<AuthenticationResult>(user);
command.Result.IsValid = true;
this.Publish(new OnAuthenticationSuccess(user.FirstName, user.LastName, user.Email, user.LoginToken, user.TokenExpiredAfter, DateTime.UtcNow));
}
}
Until now, we understand how to use basic authorization and token based authorization in TinyERP.
For more information about other articles in this series
Thank you for reading,CodeProject
Note: Please like and share to your friends if you think this is useful article, I really appreciate