Saturday, July 1, 2017

Asp.Net WebApi OWIN Authentication

Leave a Comment

After following an online tutorial to use token based authentication using OWIN, I managed to get my test app authenticating against a hard coded username/password, as the demo did.

However, now I want my model from my web application to be used.

My authentication happens, as the demo said, in this bit of code.

namespace UI {     public class AuthorisationServerProvider : OAuthAuthorizationServerProvider     {         public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)         {             context.Validated(); // Means I have validated the client.         }          public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)         {             // Here we validate the user...             var identity = new ClaimsIdentity(context.Options.AuthenticationType);             if (context.UserName == "user" && context.Password == "password")             {                 identity.AddClaim(new Claim(ClaimTypes.Role, "admin"));                 identity.AddClaim(new Claim("username", "user"));                 identity.AddClaim(new Claim(ClaimTypes.Name, "My Full Name"));                 context.Validated(identity);             }             else             {                 context.SetError("Invalid grant", "Username or password are incorrect");                 return;             }         }      } } 

I have a WebAPI controller, which I receive a model from, and ... not sure how to call the above code, from my webapi controller. At the moment, the code above expects a call to myurl/token - that was defined in the startup code.

 public class Startup     {         public void Configuration(IAppBuilder app)         {             // Enables cors origin requests.             app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);              // Config OAuth authorisation server;              var myProvider = new AuthorisationServerProvider();             OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions             {                 AllowInsecureHttp = true, // Live version should use HTTPS...                 TokenEndpointPath = new PathString("/token"),                 AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),                 Provider = myProvider             };              app.UseOAuthAuthorizationServer(options);             app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());              HttpConfiguration config = new HttpConfiguration();             WebApiConfig.Register(config);         }     } 

So, I'm guessing the url from my webapi call should be /token? So, in my (Knockout View model) code on my UI, I tried this:

 Login()     {         var data = {             username : this.login.emailAddress(),             password : this.login.password(),             RememberMe: this.login.rememberMe(),             grant_type: "password"         }          return $.ajax({             type: "POST",             data: data ? JSON.stringify(data) : null,             dataType: "json",             url: "/token",             contentType: "application/json"         }).done((reply) => {             alert("Done!");         });      } 

But, I get an exception:

“error”: “unsupported_grant_type” 

In 'Postman', I am able to authenticate the hard coded username/password.

enter image description here

But I am not sure how to wire up my api call from my UI, to authenticate.

I was hoping to create a 'Login' method on my api controller (ASP.Net WebAPI), like this:

[Route("login"), HttpPost, AllowAnonymous] public ReplyDto Login(LoginRequest login) {     ReplyDto reply = _userService.Login(login.Email, login.Password);     return reply; } 

So, my _userService checks if the user is in the database... if so, call my OAuth authentication here passing a few parameters. But not sure that's possible. Can I call my authentication from this api method? I'd need to remove the /token bit though.

2 Answers

Answers 1

You don't need to create a Login method since you already have it. It's http://localhost:1234/token. This is will generate a token if the user exists and if the password is correct. But get this behaviour you need to implement your own AuthServerProvider by deriving from OAuthAuthorizationServerProvider

public class DOAuthServerProvider : OAuthAuthorizationServerProvider 

and then you would override a method to implement your logic:

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)     {          try         {             string allowedOrigin = context.OwinContext.Get<string>(DOAuthStatic.ALLOWED_CORS_ORIGINS_KEY);              if (allowedOrigin != null)             {                 context.OwinContext.Response.Headers[DOAuthStatic.CORS_HEADER] = allowedOrigin;             }              DAuthenticationResponse authResponse = await _authRepository.Authenticate(context.UserName, context.Password);              if (!authResponse.IsAuthenticated)             {                 context.SetError(OAuthError.InvalidGrant, $"{(int)authResponse.AuthenticateResult}:{authResponse.AuthenticateResult}");                  return;             }              if (authResponse.User.ChangePasswordOnLogin)             {                 _userAuthenticationProvider.GeneratePasswordResetToken(authResponse.User);             }              IDictionary<string, string> props = new Dictionary<string, string>             {                 {                     DOAuthStatic.CLIENT_NAME_KEY, context.ClientId ?? string.Empty                 }             };              ValidateContext(context, authResponse, props);         }         catch (Exception ex)         {             DLogOAuth.LogException(ex, "DCO0407E", "OAuthServerProvider - Error validating user");              throw;         }     } 

You are almost there, you just need to do two more steps:

  1. Add the AuthorizeAttribute on your method or controller to restrict access for unauthenticated users.
  2. Add the access token you request header. If you skip this step you should get a 401 HTTP status code, meaning unauthorized. This is how you can confirm that the authorise attribute that you added in step one works.

Here is a great series of tutorials that explains everything really well: Token based authentication (way better than I have :) )

Answers 2

Change the Content Type "application/json" to "application/www-form-urlencoded"

You are Sended Data in Postman "application/www-form-urlencoded" format. But in Your Code Using "application/Json" the Content Type Mismatch. So,the Data is Not Send Proper Format.

You Can Change If it's Working Fine.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment