1

I'm building a website that will have an MVC side and will get its data from a WebAPI backend of our own, but most likely hosted on a different server (or even on Azure). We're going to use Forms authentication.

Since we want users to only need to log-in once (to the MVC website), what would be the recommended way to transparently authenticate users to the WebAPI backend with the same information that they entered on the MVC forms authentication login?

Since this authentication works based on cookies, would the best way be to call the WebApi authentication Action/Method on the Login action of the MVC app, get the auth cookie for webapi and use it on every call to the WebAPI back end?

Any guidance is greatly appreciated

1 Answer 1

4

GR7, I can't say I've ever attempted what you are doing.

Let me just point out something that is bothering me about your idea, and how I think you can make it work.

You have an ASP.NET MVC application running on one web server, and an ASP.NET WebAPI application running on another server. You want to use the cookie from one on the other. How is the cookie from the MVC application valid for the WebAPI app? Even if the username and password of the user is the same on both systems, the cookie generated by the two different applications will not be the same will it? Just to be clear, I'm not 100% sure about it, it is just a suspicion.

Here is the basis for my suspicion - let's say you run an ASP.NET MVC application on the Azure cloud, and you have it load balanced (meaning that you actually have multiple instances, each running on a different physical machine). A user connects to your website, and authenticates on that instance. He then navigates to another page on that website, and the load balancer ends up sending him to that page on another instance. I believe he will be required to reauthenticate in that case, since his cookie is not valid, even though it is the exact same MVC app. The solution to this situation is to set the same machine key on all the machines.

This is discussed on MSDN here: http://msdn.microsoft.com/en-us/library/eb0zx8fc(v=vs.100).aspx

And a microsoft KB article: http://support.microsoft.com/kb/910443

There are also some StackOverflow articles discussing this: Does Forms Authentication work with Web Load Balancers? and .NET Forms Authentication in Azure - What changes are required for multiple VMs?

So I guess you should be able to set the machine key to be the same on both web servers, and then pass the cookie from the MVC application to the WebAPI application in order to not make the user authenticate twice. Hopefully someone will correct me if I am wrong.

The other solution would be to just hold on to two cookies, one for the MVC app and the other for the Web API. You will need to figure out where to store the webapi cookie - since ASP.NET works in a stateless manner, every time the user clicks on a different MVC page this is basically a totally new transaction. So maybe you want the users browser to store both cookies. The first time he authenticates, you authenticate him on the MVC application, and also the WebAPI application, using the same username and password, and then send both cookies back to him (the MVC cookie will automatically go back to him of course). So each time he navigates to a differnt page, you get both cookies sent to your MVC application, and it will have to take one of them and call the WebAPI application with it. You may need to make sure both cookies do not have the same name (by default both will be ASPXAUTH). You can change the name of your MVC cookie in web.config using

<authentication mode="Forms">
  <forms name="MyAuthCookie" loginUrl="LoginPage.aspx" />
</authentication>

That should allow you to store 2 cookies on the user's browser and also help you distinguish between them. I'm assuming that both your MVC and WebAPI are on the same domain, otherwise the browser won't accept the WebAPI cookie (or atleast won't pass it back to you in subsequent requests).

If this answer helps, please vote, I have barely any rep :)

======================================

Edit - Adding this in reply to your question below - you wanted to know how to actually take the cookie that the WebAPI gives to your MVC app, and return that to the user's browser. I will start with sending the http post request with your credentials to your webapi from your mvc app, just so you are clear on everything.

Let's use JSON to send login information as part of the HTTP request to your Web API server from your MVC app. Crate a model in the Models folder of your MVC app that is something like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace SitterWebsite.Models
{
    public class MyJsonLoginModel
    {
        public string UserName;
        public string Password;
        public bool RememberMe;
    }
}

Then in your Login() method in ActionController.cs, you can add something like this to make the request

string loginapibaseaddress = "http://mywebapiurl.com/";
string loginapiaddress = "api/AccountAPI/SignMeIn";

MyJsonLoginModel mydatamodel = new MyJsonLoginModel()
{
        UserName = "gary",
        Password = "password",
        RememberMe = false,
};

// Create the JSON formatter.
MediaTypeFormatter jsonFormatter = new JsonMediaTypeFormatter();

// Use the JSON formatter to create the content of the request body.
HttpContent content = new ObjectContent<MyJsonLoginModel>(mydatamodel, jsonFormatter);

// I am going to return the cookie received from the Web API controller to the browser
// I will obtain it from the HTTP POST request to the WebAPI in the form of a Cookie object
// I will then need to convert it to an HttpCookie object
Cookie cookietosendback = new Cookie();   // cookie of type System.Net.Cookie
HttpCookie httpcookietosendback = new HttpCookie("will_name_later");   // cookie of type System.Web.HttpCookie


// Create a new cookie container. 
// We will attach this cookie container to the HTTP request
// The Web API auth cookie will automatically be put into this container
CookieContainer cookie_container = new CookieContainer();

HttpClientHandler handler = new HttpClientHandler();
handler.CookieContainer = cookie_container;
HttpClient loginclient = new HttpClient(handler);

// Set the base address of the client
loginclient.BaseAddress = new Uri(loginapibaseaddress);

// Set the web api address of the client
Uri loginapiaddressuri = new Uri(loginapibaseaddress+loginapiaddress);

// Send an HTTP POST request
HttpResponseMessage response = loginclient.PostAsync(loginapiaddressuri, content).Result;


Cookie mycookie;

if (response.IsSuccessStatusCode) 
{

    // Now let's access the cookies from the cookie container since it will be automatically populated with any
    // cookies returned by our http request (ie. any cookies in the http response). 

    IEnumerable<Cookie> responseCookies = cookie_container.GetCookies(loginapiaddressuri).Cast<Cookie>();

    foreach (Cookie cookie in responseCookies)
    {
    if cookie.Name.Equals('.ASPXAUTH')
        cookietosendback = cookie
    }

    // We want to return the cookie to the users browser
    // However the HttpContext.Response.Cookies.Add() method needs an HttpCookie object, not a Cookie object
    // So we need to convert the Cookie to an HttpCookie
    httpcookietosendback.Name = "GaryCookie"; // changing name since both MVC and WebAPI name their cookie .ASPXAUTH
    httpcookietosendback.Value = cookietosendback.Value;
    httpcookietosendback.Path = cookietosendback.Path;
    httpcookietosendback.Expires = cookietosendback.Expires;
    httpcookietosendback.Domain = cookietosendback.Domain;  
    // Note - if the domain of your WebAPI is different from the MVC app, you might want to change it in
    // above statement, otherwise the browser will either not accept a cookie from another domain, or it will
    // definitely not pass it to the mvc app in your next request

    this.ControllerContext.HttpContext.Response.Cookies.Add(httpcookietosendback);

}
else
{
    // Http post to webapi failed
}

So now when you go to the login page and type out your credentials and hit submit, you will not only get the MVC cookie that you normally get, but also the WebAPI cookie. It will be named "GaryCookie" as per my code above. Everytime you go to another page on your website, your browser will request the page and send both cookies to your mvc app. If you wish to call other WebAPI methods, you will now need to do the reverse of what I have just done, meaning take the modified WebAPI cookie "GaryCoookie" and rename it back to what it originally was. And then send it with the headers when making a GET or POST request on your WebAPI methods.

You should also set the domain of the cookie to match that of MVC if your webapi and mvc app are not on the same domain. Otherwise your browser will not send the cookie to the MVC app if you request another page.

And by the way, I tested all of this just now, so it works.

Sign up to request clarification or add additional context in comments.

6 Comments

Hi Gary, thanks for the reply. I didn't say I wanted to use the same cookie for both systems, I said my idea was that, when the user logged to the MVC website, on the LogOn action, I have access to the username and password in plain text, at which point I was thinking I could do a webapi call with the same credentials to get the webapi auth cookie and then use that cookie for any further webapi calls. have you ever done that?
GR7, thanks for clarifying. What you are saying is correct. This is basically what I said in the second part of my response (which starts with "The other solution..."). When your user logs into the MVC website, you will be able to acces the username and password. Let's assume you are using forms authentication with the default C# code that is generated for you. If you look in AccountController.cs, you see the login action. public ActionResult Login(LoginModel model, string returnUrl){ }
Within that method, you can access the username and password using model.UserName and model.Password. You should then be able to do a call to your WebAPI and get an authentication cookie from it.
Note however that this auth cookie has been sent by WebAPI to your MVC app, it has NOT been sent to your user's browser. You need to make sure you also write some code here to return the cookie to the browser! Otherwise it is not the user that is logged into the webAPI, it is only your MVC application that has an authentication cookie for the webapi. And it will delete that cookie once the transaction is complete, so on future requests you will not be logged into the webAPI. That is why I was talking earlier about sending the webapi auth cookie to the user along with the mvc auth cookie.
of course I intended to add the obtained webapi cookie to be used for any further requests, however, that's what I'm not clear on yet. How would I add the obtained cookie to the user so that it's sent along any further ajax webapi requests?
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.