7

Hi I am trying to set up OAuth bearrer token authentication in my ASP.NET Web API 2 project. I have two project one will be the WEB API Project and the other a SPA project.

Here is what I have done so far:

I have created the OWIN Startup class:

[assembly: OwinStartup(typeof(CodeArt.WebApi.App_Start.Startup))]

namespace CodeArt.WebApi.App_Start

{
    public class Startup
    {
        static Startup()
        {
            PublicClientId = "self";

        UserManagerFactory = () => new UserManager<UserModel>(new UserStore<UserModel>());

        OAuthOptions = new OAuthAuthorizationServerOptions {
            TokenEndpointPath = new PathString("/Token"),
            Provider = new OAuthAuthorizatonServer(PublicClientId, UserManagerFactory),
            AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            AllowInsecureHttp = true
        };
    }

    public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }

    public static Func<UserManager<UserModel>> UserManagerFactory { get; set; }

    public static string PublicClientId { get; private set; }
    public void Configuration(IAppBuilder app)
    {
        ConfigureAuth(app);
    }

    public void ConfigureAuth(IAppBuilder app)
    {
        app.UseCookieAuthentication(new CookieAuthenticationOptions());
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalBearer);
        app.UseOAuthBearerTokens(OAuthOptions);
    }
}

I have configured Web API to use only bearer token authentication:

    private static void ConfigureBearerTokenAuthentication(HttpConfiguration config)
    {
        config.SuppressDefaultHostAuthentication();
        config.Filters.Add(new HostAuthenticationFilter(Startup.OAuthOptions.AuthenticationType));

    }

I have configured WEB API to support CORS:

    private static void ConfigureCrossOriginResourseSharing(HttpConfiguration config)
    {
        var cors = new EnableCorsAttribute("*", "*", "*");
        config.EnableCors(cors);
    }

I have created the OAuthAuthorizationServerProvider class.From this class I only managed to make my code call this method:

   public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        if(context.ClientId == null)
        {
            context.Validated();
        }

        return Task.FromResult<object>(null);
    }

The if condition inside of it always gets executed.

On my spa project I have the following:

This is my viewModel:

 var vm = {
        grant_type: "password",
        userName: ko.observable(),
        password: ko.observable()
};

When the login button gets clicked I call this function:

     var http = {
          post:function(url, data) {
             return $.ajax({
                url: url,
                data: data,
                type: 'POST',
                contentType: 'application/json',
                dataType: 'jsonp'
            });
        }
     }

function loginClick() {
        var model = ko.mapping.toJS(vm.loginModel);
        var rez = $.param(model);

        http.post("http://localhost:3439/Token", rez)
            .done(function (data) {
                console.log(data);
            })
            .fail(function(eror, stuff, otherstuff) {
                console.log(eror);
                console.log(stuff);
                console.log(otherstuff);
            });
    }

My first attempt I have set the post calls dataType to json and I got this errors:

OPTIONS ...:3439/Token 400 (Bad Request) jquery.js:7845

OPTIONS ...:3439/Token No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin '...:3304' is therefore not allowed access. jquery.js:7845

XMLHttpRequest cannot load ...3439/Token. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin '...3304' is therefore not allowed access.

The 3 dots represent http://localhost.

The second time arround I set it datatype to jsonp and I got back an error that stated unsupported "unsupported_grant_type".

Both calls make it to ValidateClientAuthentication that I mentioned above but they are both sent back as a failed request.

Now I am guessing that the problem is more related to how I am sending data instead of the grand_type because the SPA template in Visual Studion set's the grant type to grant_type: "password" like I did.

Also I have read that I have to serialize the data not send it in json in order for this to work here is the exact json serialized data that get's sent: "grant_type=password&userName=aleczandru&password=happynewYear&moduleId=models%2FappPostModels%2FloginModel"

The model id property get's set to all my object in my SPA template by Durandal Framework.

Can anyone tell me what I am doing wrong I have been trying to figure this out for the last two days?

aleczandru
  • 5,319
  • 15
  • 62
  • 112

2 Answers2

18

Add the following line of code to GrantResourceOwnerCredentials, which will add the header to the response.

context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });

for more information refer to: web-api-2-0-cors-and-individual-account-identity

Koraktor
  • 41,357
  • 10
  • 69
  • 99
Omar.Alani
  • 4,050
  • 2
  • 20
  • 31
  • Interesting I would have tought that enabling CORS at the WEB.API level would be enought – aleczandru Feb 24 '14 at 10:57
  • 1
    I've created a code project tutorial about how to use the new Web API 2 with AngularJS client http://www.codeproject.com/Articles/742532/Using-Web-API-Individual-User-Account-plus-CORS-En . hope that will give more details. – Omar.Alani Mar 12 '14 at 10:08
  • I just tried this without any success, i'm still getting the error. Any other suggestions? – Robin Karlsson Mar 24 '14 at 19:39
  • Could you please give more details, have you tried to download the project in the tutorial I've mentioned ? [link](http://www.codeproject.com/Articles/742532/Using-Web-API-Individual-User-Account-plus-CORS-En) – Omar.Alani Mar 24 '14 at 23:38
  • 9
    I found another solution for this by using app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); – Robin Karlsson Mar 25 '14 at 10:50
  • Worked for me with a slight variation: context.OwinContext.Response.Headers.Add( "Access-Control-Allow-Origin" , new[ ] { "http://... origin's URL" } ); context.OwinContext.Response.Headers.Add( "Access-Control-Allow-Credentials", new[ ] { "true" } ); – Jelgab Jan 27 '15 at 23:05
8

Like Robin Karlsson said, you should use:

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

in your Startup configuration.

And make sure it's the only cors statement (don't mix them) and the first statement in your Startup.

Elger Mensonides
  • 6,930
  • 6
  • 46
  • 69
  • 1
    Make sure to install the various Cross-Origin support nuget packages – JasonCoder Dec 18 '14 at 19:16
  • I have tried enabling CORS both globally (via OWIN app.UseCors as you say) and with separate OWIN and Web API policies. The "/token" URL is being processed by my Policy Resolver properly and I STILL am not seeing an Access-Control-Allow-Origin response from the server. I am a skip and hop away from adding the header globally in web.config. What is going on? –  Mar 02 '15 at 04:21
  • Some browsers (like chrome) give the access allowed origin error if there's an error on the server. I get them sometimes while developing. Are you sure there's not something else going on? Try to switch of all authentication and access simple testcontroller. Check your logs and traces. – Elger Mensonides Mar 03 '15 at 08:53