Bad Request (400) when using Web API Token Authentication from Angular JS

試著忘記壹切 提交于 2019-12-01 06:37:46

Ok, this will be a long answer but hold on to the end:)

Step 1: Remove the Global.asax

The Global.asax is not needed when you run on the Owin pipeline. The Startup.cs is what I would say Owins Global.asax. They basically fills the same purpose so go ahead and remove it.

Step 2: Remove the Cors handling in the WebApiConfig.cs

This code is not needed as you already declare it in the Startup.cs.

app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

Your WebApiConfig.cs will then look like this

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {            
        // Web API routes
        config.MapHttpAttributeRoutes();
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
        config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
        config.Formatters.Remove(config.Formatters.XmlFormatter);
    }
}

Step 3: Add Web Api and bearer token autentication to the Owin pipeline in Startup.cs

Instead of binding the WebApiConfig in the Global.asax you attach it to the pipeline. Also apply the bearer token handling to the pipeline.

Your Startup.cs will then look like this

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
        OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions
        {
            AllowInsecureHttp = true,
            TokenEndpointPath = new PathString("/Token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
            Provider = new AuthorizationServerProvider()
        };
        app.UseOAuthAuthorizationServer(options);
        app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

        //Register the web api to the pipeline 
        HttpConfiguration config = new HttpConfiguration();
        WebApiConfig.Register(config);
        app.UseWebApi(config);
    }
}

Step 4: Add headers to the request in the AuthorizationServerProvider.cs

public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
    public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        context.Validated(); 
    }
    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        SetContextHeaders(context);
        string userId = context.UserName;
        string password = context.Password;

        EmployeeAccessBLL chkEmpAccessBLL = new EmployeeAccessBLL();
        EmployeeAccessViewModel vmEmployeeAccess = chkEmpAccessBLL.CheckEmployeeAccess(Convert.ToInt32(userId), password);

        if(vmEmployeeAccess != null)
        {
            var identity = new ClaimsIdentity(context.Options.AuthenticationType);
            identity.AddClaim(new Claim("username", vmEmployeeAccess.EmpName));
            context.Validated(identity);
        }
        else
        {
            context.SetError("invalid_grant", "Provided username and password is incorrect");
            return;
        }
    }
    private void SetContextHeaders(IOwinContext context)
    {
        context.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
        context.Response.Headers.Add("Access-Control-Allow-Methods", new[] { "GET, PUT, DELETE, POST, OPTIONS" });
        context.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "Content-Type, Accept, Authorization" });
        context.Response.Headers.Add("Access-Control-Max-Age", new[] { "1728000" });
    }
} 

Step 5: Make a correct request to the Oauth server

The request to the oauth server need to be of content type x-www-form-urlencoded which basically is a string. I also added promise instead of callbacks which is what the $q does. IMO I think the promise is more clear to read

TIP: don't send the credentials in clear text. You could endecode them to Base64 string with btoa(password) and then decode it in your backend.

angular.module('appHome').factory('MetadataOrgFactory', ['$http', function ($http) {

    var url = 'http://localhost:60544';    
    var dataFactory = {};  
    dataFactory.login = function (userName, password) {
        var deferred = $q.defer();

        $http({
            method: 'POST',
            url: url + '/Token',
            processData: false,
            contentType: 'application/x-www-form-urlencoded',
            data: "grant_type=password&username=" + userName + "&password=" + password,
        }).
            success(function (data) {
                deferred.resolve(data);
            }).
            error(function (message, status) {              
                console.log(message);
                deferred.reject(message, status);
            });

        return deferred.promise;
    };  
    return dataFactory;
}]);

Step 6: Make a login request from your controller

angular.module('appHome', []);
angular.module('appHome').controller("ctrlLogin", ['$scope', 'MetadataOrgFactory', '$location', function ($scope, MetadataOrgFactory, $location) {
    $scope.Login = function () {

        MetadataOrgFactory.postLoginCall($scope.md_empnumber, $scope.md_password).then(
            function (result) {
                //success
            },
                function (error, statusCode) {
                    console.log(error);
                }
            );;
    }
}]);

That's it.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!