[TinyERP]Basic authentication (user name/password) with OWIN

0
42

Overview

In this article, we will discuss briefly on how to perform the basic authentication(username/password) in TinyERP using Owin.

I expect you will check out the code form github (url: https://github.com/techcoaching/TinyERP, develop branch) and check it for more information. That is why the pasted code in this article only contains major part.

For now, We will not talk about how security was provided by user/password authentication, it was not the purpose of this article.

With basic authentication, client will send user-name/password in each request to server. This is a good point that can let you understand how to integrate authentication into TinyERP as many questions from readers.

For the reason, Why do we need to authorize request from client/ user/ agent? This topic can be found internet easily. Just simple, System need to know who is accessing to sensitive resource. So it can continue or reject the request.

With simple authentication (user name/password), beside normal headers (accept, content-type, …) when sending request to API, client also sends credential information.

On the Api, We validate request base on this information, this makes sure that resource was accessed by expected users/agents.

If the credential was invalid (invalid user name or password, …), the api should return 401 (Unauthorized) to client.

Otherwise, the request was processed and expected result will be returned to client/ user/ agent.

Let try to implement this in TinyERP.

How to implement in TinyERP?

The sample code for this was available at https://github.com/techcoaching/TinyERP (in develop branch)

We use “Authorize” attribute to restrict access to system resource. In this case, I assume that “http://localhost/api/permissions” was only accessed by valid credential.

namespace App.Api.Features.Security
{
 [RoutePrefix("api/permissions")]
 public class PermissionsController : BaseApiController
 {
 [Authorize()]
 [HttpGet]
 [Route("")]
 [ResponseWrapper()]
 public IList<PermissionAsKeyNamePair> GetPermissions()
 {
 
 }
 }
}

We need to config Owin in order to make this attribute works as expected. check source-code of “<solution>\App.Api\Global.asax”, we have the following:

[assembly: Microsoft.Owin.OwinStartup(typeof(App.Api.WebApiApplication))]
namespace App.Api
{
 public class WebApiApplication : App.ApiContainer.ApiApplication
 {
 public void Configuration(IAppBuilder app)
 {
 this.Config<IAppBuilder>(app);
 }
 }
}

This was Owin setup that Configuration method will be called and this.Config<IAppBuilder>(app) was also called.

We need to specify “owin:AutomaticAppStartup” in web.config file (in appSettings part):

<add key="owin:AutomaticAppStartup" value="true" />

The reason why “this.Config<IAppBuilder>(app)”, Please read Manage Application Lifecycle

Look at “App.Security.Owin\ConfigAuthTask.cs”, we have:

namespace App.Security.Owin
{
 public class ConfigAuthTask : BaseTask<TaskArgument<IAppBuilder>>, IConfigAppTask<TaskArgument<IAppBuilder>>
 {
 public override void Execute(TaskArgument<IAppBuilder> arg)
 {
 if (!this.IsValid(arg.Type)) { return; }

 arg.Data.Use<UserNamePwdAuthMiddleware>(new UserNamePwdAuthOptions());
 }
 }
}

This task will be called automatically by system in application config phase. This will config middle-ware and option classes.

Please search on the internet to know what is middle-ware/options classes in Owin for more information.

The key thing was located in OwinAuthenticationHandler class:

namespace App.Security.Owin.UserNamePwd
{
 internal class OwinAuthenticationHandler : AuthenticationHandler<UserNamePwd.UserNamePwdAuthOptions>
 {
 protected async override Task<AuthenticationTicket> AuthenticateCoreAsync()
 {
 UserNameAndPwdAuthenticationResult authorise = this.Authorise(this.Request.Headers);
 if (authorise == null || !authorise.IsValid)
 {
 return null;
 }
 
 
 }

 private UserNameAndPwdAuthenticationResult Authorise(IHeaderDictionary headers)
 {
 string[] acceptLanguageValues;
 bool acceptLanguageHeaderPresent = headers.TryGetValue(Constants.AUTHENTICATION_TOKEN, out acceptLanguageValues);
 if (!acceptLanguageHeaderPresent)
 {
 return null;
 }
 string[] elementsInHeader = acceptLanguageValues.ToList()[0].Split(new string[] { Constants.AUTHENTICATION_TOKEN_SEPERATOR }, StringSplitOptions.RemoveEmptyEntries);
 string userName=elementsInHeader[0];
 string pwd=elementsInHeader[1];

 ICommandHandlerStrategy commandHandlerStrategy = CommandHandlerStrategyFactory.Create<User>();
 UserNameAndPwdAuthenticationRequest request = new UserNameAndPwdAuthenticationRequest(userName, pwd);
 commandHandlerStrategy.Execute(request);
 return request.Result;
 }
 }
}

We will retrieve credential information (user name and password) from request and validate in Authorise method.

Then new instance of UserNameAndPwdAuthenticationRequest command class will be created and sent out.

ICommandHandlerStrategy commandHandlerStrategy = CommandHandlerStrategyFactory.Create<User>();
UserNameAndPwdAuthenticationRequest request = new UserNameAndPwdAuthenticationRequest(userName, pwd);
commandHandlerStrategy.Execute(request);

We have command handler for this command in “App.Security.Command.Impl\UserNameAndPwdAuthCommandHandler.cs”:

namespace App.Security.Command.Impl
{
 internal class UserNameAndPwdAuthCommandHandler : BaseCommandHandler, IUserNameAndPwdAuthCommandHandler
 {
 public void Handle(UserNameAndPwdAuthenticationRequest command)
 {
 IUserRepository repository = IoC.Container.Resolve<IUserRepository>(uow);
 User user = repository.GetActiveUser(command.UserName, EncodeHelper.EncodePassword(command.Password));
 if (user == null)
 {
 
 }
 
 }
 }
}

Ok, Now let try to run and test this function.

We have “techcoaching/123” user in database:

Run the api and post GET request to “http://localhost:22383/api/permissions” without credential information:

Let try to send “GET http://localhost:22383/api/permissions” with “techcoaching|123” credential, we receive the list of permissions:

And content of response:

For now, We can understand how to authorize  request from user with basic authentication (user name and password) in TinyERP.

In next article, we will discuss more complex authentication.

Thank for reading.

Note: Please like and share to your friends if you think this is usefull article, I really appreciate

LEAVE A REPLY