Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asp.Net WebApi OWIN Authentication

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.

like image 319
Craig Avatar asked Mar 02 '17 04:03

Craig


2 Answers

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 :) )

like image 117
Radu Cojocari Avatar answered Nov 15 '22 15:11

Radu Cojocari


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.

like image 42
umasankar Avatar answered Nov 15 '22 13:11

umasankar