Pregunta

I'm creating an application in 2 parts. On one server is a .net Webapi2 using Owin. On another server is an MVC5 website with currently no login that will act as a front end for the api. It would also be a nice selling point to show that the app itself is an example of what a client can develop since it relies on the same api. I put the user authentication stuff in the api because I need 3rd parties to be able to develop their own front end apps using the api.

What I'm trying to accomplish (in theory) I need to have a user submit their login information on the front end it will authenticate them via the resourceownergrant type and recieve results that allow the front end to create a cookie that includes the accesstoken and the identityuser / roles. As long as this cookie exists the mvc app would make calls to the api using the accesstoken. The MVC app and API would both be able to use the [Authorize] attribute.

What I have so far I have the api up and working, I can post "grant_type=password&username=testuser&password=password123" and I receive something like this in json

{
  "access_token":"-longasstokenhere-",
  "token_type":"bearer",
  "expires_in":1209599,
  "userName":"testuser",
  ".issued":"Thu, 03 Apr 2014 16:21:06 GMT",
  ".expires":"Thu, 17 Apr 2014 16:21:06 GMT"
}

the web api's response also has a header of

set-cookie: -Long-assserializedcookiestuffhere-

My question is how to connect my mvc app.

in my mvc5 app I've got a startup for owin with this set in the configureauth

app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                CookieName = "MyCookie",
                LoginPath = new PathString("/Account/Login")
            });

I have the [Authorize] attribute set on a test page that when I visit, it correctly redirects me to the login page. The piece I'm missing is how to make it so that clicking login makes the website post back to the api and create an Owin Cookie that the website will use to allow a user past the [authorize] attibute. I would also like it to contain the identityuser as well so the web app will automatically have the user info without having to post back to the api for it on every call. I dont know if I can grab the cookie from the return result somehow or what.

Also if theirs a better way I'm open to suggestions.

Heeelp!?

¿Fue útil?

Solución

I had the exact same requirement. I tried to get the MVC5 CookieAuthentication to work, but it wouldn't let me use my own cookie value. But, your WebAPI should not return a set-cookie. WebAPI should be RESTful, and require the client to pass a bearer token on every request.

So, here's my solution. The username and password are sent to an external API, which return a JSON Web Token. The token from the API is stored in a cookie. You could do that in JavaScript, or in an MVC Account controller. That cookie is checked by the MVC app, and if it exists, the cookie indicates proof that the user is logged in to the MVC app, too.

In JavaScript, you pull that token from the cookie, and add it to all requests to the API in the Authorization header as Bearer token. Also, the MVC app can use the value of the cookie (the token) to access all of the user's claims. To logout, just expire the cookie.

First, wire up the app to use Bearer token, with our custom provider

// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
    new JwtBearerAuthenticationOptions
    {
        AllowedAudiences = audienceId.ToArray(),
        IssuerSecurityTokenProviders = providers.ToArray(),
        Provider = new CookieOAuthBearerProvider("MyCookieName")
        {
            LoginPath = new PathString("/Account/Login")
        }
    }
);

Now, I had to use a custom provider, because the Bearer token is stored in the cookie, not in the standard header. I also need to redirect to a login page, rather that simply issuing a 401.

public class CookieOAuthBearerProvider : IOAuthBearerAuthenticationProvider
{
    public PathString LoginPath {get; set;}

    public string CookieName { get; set; }

    public CookieOAuthBearerProvider(string cookieName)
    {
        if(string.IsNullOrWhiteSpace(cookieName)) {
            throw new ArgumentNullException("cookieName");
        }
        else {
            this.CookieName = cookieName;
        };
    }

    public Task ApplyChallenge(OAuthChallengeContext context)
    {
        if (this.LoginPath.HasValue)
        {
            context.Response.Redirect(this.LoginPath.Value);
        }
        return Task.FromResult<object>(null);
    }

    public Task RequestToken(OAuthRequestTokenContext context)
    {
        string token = context.Request.Cookies[this.CookieName];
        if (!string.IsNullOrEmpty(token))
        {
            context.Token = token;
        }
        return Task.FromResult<object>(null);
    }

    public Task ValidateIdentity(OAuthValidateIdentityContext context)
    {
        // prove that the cookie token matches this site using context.Ticket.Identity
        return Task.FromResult<object>(null);
    }
}

Then, anywhere else in your MVC app, you can get the Claims by simply saying:

ClaimsPrincipal.Current
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top