1

I have a mvc 5 app that uses forms authentication but the real Authentication of user happens using bearer token in web api . I'm adding token details in the cookie so the website is always authenticated. MVC and Web api are in same project. Web api hosted using Owin.

here is my code snippet.

startup.cs

 public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {

        //Configure authorization
        ConfigureOAuth(app);

        //register WebAPI
        app.UseWebApi(ConfigureWebApiRoutes());

    }
}

startup.auth.cs

          // Enable the application to use a cookie to store information for the signed in user
          app.UseCookieAuthentication(new CookieAuthenticationOptions
          {
            AuthenticationType =       DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Login/Login"),
            CookieHttpOnly = true,
            //AuthenticationMode = AuthenticationMode.Passive,
            CookieName = "YetAnotherTodo.WebApi.Auth",
            //#if DEBUG
            //                CookieSecure = CookieSecureOption.Never
            //#endif
        });


        // Use a cookie to temporarily store information about a user      logging in with a third party login provider
                         app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);



        OAuthBearerOptions = new OAuthBearerAuthenticationOptions();

        // using OAuth authentication server as authentication middle ware  and Token Generation           
        app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
        {                
            TokenEndpointPath = new PathString("/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
            Provider = new SimpleAuthorizationServerProvider(),
            AuthorizeEndpointPath = new  PathString("/api/Account/ExternalLogin"),
            #if DEBUG
             AllowInsecureHttp = true
            #endif
        });
        app.UseOAuthBearerAuthentication(OAuthBearerOptions);

code in MVC Login Controller

        [AllowAnonymous]
        [HttpPost]
        public async Task<ActionResult> Login(LoginViewModel model, string redirectUrl = null)
        {
         if (!ModelState.IsValid) return View(model);
         try
         {
         if (string.IsNullOrWhiteSpace(redirectUrl))
            {
                redirectUrl = "~/Home";
            }
            var result = await WebApiService.Instance.AuthenticateAsync<LogInResult>(model.UserName, model.Password);

            //Let's keep the user authenticated in the MVC webapp.
            //By using the AccessToken, we can use User.Identity.Name in the MVC controllers to make API calls.
            FormsAuthentication.SetAuthCookie(result.AccessToken, model.RememberMe);

            //Create an AuthenticationTicket to generate a cookie used to authenticate against Web API.
            //But before we can do that, we need a ClaimsIdentity that can be authenticated in Web API.
            var claims = new[]
            {
                new Claim(ClaimTypes.Name, model.UserName),
                //Name is the default name claim type, and UserName is the one known also in Web API.
                new Claim(ClaimTypes.NameIdentifier, model.UserName)
                //If you want to use User.Identity.GetUserId in Web API, you need a NameIdentifier claim.
            };

            //Generate a new ClaimsIdentity, using the DefaultAuthenticationTypes.ApplicationCookie authenticationType.
            //This also matches what we've set up in Web API.
            var claimsIdentity = new ClaimsIdentity(claims,DefaultAuthenticationTypes.ApplicationCookie);
            var authProperties = new AuthenticationProperties
            {
                ExpiresUtc = result.Expires,
                IsPersistent = model.RememberMe,
                IssuedUtc = result.Issued,
                RedirectUri = redirectUrl
            };
            var authTicket = new AuthenticationTicket(claimsIdentity, authProperties);

            //And now it's time to generate the cookie data. This is using the same code that is being used by the CookieAuthenticationMiddleware class in OWIN.
            byte[] userData = DataSerializers.Ticket.Serialize(authTicket);

            //Protect this user data and add the extra properties. These need to be the same as in Web API!
            //byte[] protectedData = MachineKey.Protect(userData, new[] { "Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware", DefaultAuthenticationTypes.ApplicationCookie, "v1" });

            //base64-encode this data.
            string protectedText = TextEncodings.Base64Url.Encode(userData);

            //And now, we have the cookie.
            Response.SetCookie(new HttpCookie("YetAnotherTodo.WebApi.Auth")
            {
                HttpOnly = true,
                Expires = result.Expires.UtcDateTime,
                Value = protectedText
            });

Code in my provider that generates token

           AuthenticationTicket ticket;
        var cookiesIdentity = GenerateCookiesIdentity(context, user, out ticket);
        context.Validated(ticket);
        context.Request.Context.Authentication.SignIn(cookiesIdentity);

I was able to login and able to get ticket from token server but on subsequent request or redirect to home page after logging in , I'm still getting 401 error.

This is kinda combinations of these two blogs/tutorials : Blog 1 and Blog 2

Kalyan
  • 307
  • 2
  • 14
  • Are you getting 401 responses in the Web API part or in the MVC client application? The statement `FormsAuthentication.SetAuthCookie(result.AccessToken, model.RememberMe);` generates the authentication cookie for MVC, so you should see a cookie with a name like `.ASPXAUTH` – Wesley Cabus Nov 02 '15 at 07:49
  • 401 is for MVC only. I was able to retrieve token and talk to web api without issues(tested again). I'm in process of testing basic cookie authentication(without token being involved) to check if owin formsauthentication is working for my MVC without any bells and whistle. – Kalyan Nov 02 '15 at 15:27
  • In the web.config file of the MVC project, do you have an authentication section like this under system.web? ` ` After logging on in MVC, you should see an .ASPXAUTH cookie. If that's not there, forms authentication is not working properly. – Wesley Cabus Nov 02 '15 at 20:00
  • yes removed forms module from system.web and also from system.webserver. i think the problem is with the MVC project itself. it was updated from mvc4 to mvc5. I'm now recreating project and testing it. will keep you updated. Thank you very much for following up. – Kalyan Nov 02 '15 at 20:05
  • Yeah, that might be it. It's also best to drop the old Forms Authentication bits and move onwards by using newer techniques like ASP.NET Identity, Thinktecture Identityserver or Auth0. – Wesley Cabus Nov 02 '15 at 22:15
  • so i started of with new project and found that adding config.SuppressDefaultHostAuthentication(); is breaking and always returning 401 for MVC. – Kalyan Nov 04 '15 at 16:14
  • SuppressDefaultHostAuthentication does what it says: it disables the default host authentication mechanism, so in this case it might be disabling Forms Authentication in MVC. – Wesley Cabus Nov 04 '15 at 20:52
  • yeah, I want to keep forms auth for mvc but disable for web api. web api to use only bearer token. – Kalyan Nov 04 '15 at 21:01
  • 1
    If the Web API part is inside the same project as MVC, you can use app.Map() to achieve this: http://stackoverflow.com/questions/21878446/suppressdefaulthostauthentication-in-webapi-owin-also-suppressing-authentication – Wesley Cabus Nov 04 '15 at 21:07
  • thank you Wesley, using app.Map() worked. i had to change my web api routing little bit but that is not bad at all. – Kalyan Nov 09 '15 at 20:27
  • @WesleyCabus - I read your blog and it is excellent. I have two questions. How would I go about refreshing the token from the MVC app or validating it? Excuse my question if its mundane. – rizan Apr 20 '18 at 06:35

0 Answers0