9

I have a Web API 2 Project that is referenced by a SPA JavaScript application.

I'm using OWIN to authenticate the requests and upon login with Forms authentication, however, on each send back to the server my resources are not authenticated after I login.

App_Start/WebApiConfig.cs

namespace API
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services
            // Configure Web API to use only bearer token authentication.
            config.SuppressDefaultHostAuthentication();
            config.Filters.Add(new HostAuthenticationFilter(Startup.OAuthBearerOptions.AuthenticationType));

            config.EnableCors(new EnableCorsAttribute(
                origins: "*", headers: "*", methods: "*"));

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            // Use camel case for JSON data.
            config.Formatters.JsonFormatter.SerializerSettings.ContractResolver =
                new CamelCasePropertyNamesContractResolver();
        }
    }
}

/Startup.cs

[assembly: OwinStartup(typeof(API.Startup))]

namespace API
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);
        }
    }
}

App_Start/Startup.Auth.cs

namespace API
{
    public partial class Startup
    {
        static Startup()
        {
            OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
        }

        public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }

        public void ConfigureAuth(IAppBuilder app)
        {
            app.UseCookieAuthentication(new CookieAuthenticationOptions());
            app.UseOAuthBearerAuthentication(OAuthBearerOptions);
        }
    }
}

Controllers/AccountController.cs

namespace API.Controllers
{
    public class AccountController : ApiController
    {

        public AccountController()
        {
            HttpContext.Current.Response.SuppressFormsAuthenticationRedirect = true;
        }

        [HttpPost]
        [AllowAnonymous]
        [Route("api/account/login")]
        [EnableCors(origins: "*", headers: "*", methods: "*", SupportsCredentials = true)]
        public HttpResponseMessage Login(LoginBindingModel login)
        {
            var authenticated = false;
            if (authenticated || (login.UserName == "a" && login.Password == "a"))
            {
                var identity = new ClaimsIdentity(Startup.OAuthBearerOptions.AuthenticationType);
                identity.AddClaim(new Claim(ClaimTypes.Name, login.UserName));

                AuthenticationTicket ticket = new AuthenticationTicket(identity, new AuthenticationProperties());
                var currentUtc = new SystemClock().UtcNow;
                ticket.Properties.IssuedUtc = currentUtc;
                ticket.Properties.ExpiresUtc = currentUtc.Add(TimeSpan.FromMinutes(30));

                var token = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);
                var response = new HttpResponseMessage(HttpStatusCode.OK)
                {
                    Content = new ObjectContent<object>(new  
                    { 
                        UserName = login.UserName,
                        AccessToken = token
                    }, Configuration.Formatters.JsonFormatter)
                };

                FormsAuthentication.SetAuthCookie(login.UserName, true);

                return response;
            }

            return new HttpResponseMessage(HttpStatusCode.BadRequest);
        }

        [HttpGet]
        [Route("api/account/profile")]
        [Authorize]
        public HttpResponseMessage Profile()
        {
            return new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new ObjectContent<object>(new
                {
                    UserName = User.Identity.Name
                }, Configuration.Formatters.JsonFormatter)
            };
        }
    }
}

Then I invoke it with JavaScript like:

       $httpProvider.defaults.withCredentials = true;

       login: function(user, success, error) {
            return $http.post('/api/account/login', user);
        },

        profile:function(){
            return $http.get('/api/account/profile');
        }

My cookies are set on the browser:

ASPXAUTH 040E3B4141C86457CC0C6A10781CA1EFFF1A32833563A6E7C0EF1D062ED9AF079811F1600F6573181B04FE3962F36CFF45F183378A3E23179E89D8D009C9E6783E366AF5E4EDEE39926A39E64C76B165

but after login, further requests are deemed unauthorized...

Status Code:401 Unauthorized

I feel like I'm REALLY close just missing one little piece, anyone got any ideas?

amcdnl
  • 8,470
  • 12
  • 63
  • 99

2 Answers2

4

Are you using Bearer token from your app? If you didn't use it and just want to use cookie, please remove following code:

        // Web API configuration and services
        // Configure Web API to use only bearer token authentication.
        config.SuppressDefaultHostAuthentication();
        config.Filters.Add(new HostAuthenticationFilter(Startup.OAuthBearerOptions.AuthenticationType));

The code above will only allow bearer authentication for web api.

And you may also remove app.UseOAuthBearerAuthentication(OAuthBearerOptions); to remove bearer authentication middleware from OWIN pipeline.

If you want to use bearer token in your app, you need to set the token before sending ajax request in browser.

LeftyX
  • 35,328
  • 21
  • 132
  • 193
Hongye Sun
  • 3,868
  • 1
  • 25
  • 18
4

Way too long to post but added all the details on how to set this up on github gist.

Richard Ev
  • 52,939
  • 59
  • 191
  • 278
amcdnl
  • 8,470
  • 12
  • 63
  • 99