Question

I am working on an MVC site that has some pages that need authentication and others that don't. This is determined using the Authorize and AllowAnonymous attributes in a pretty standard way. If they try to access something restricted they get redirected to the login page.

I'm now wanting to add the functionality to automatically log them in using an encrypted token passed in the querystring (the link will be in emails sent out). So the workflow I want now is that if a request goes to a page that is restricted and there is a login token in the querystring I want it to use that token to log in. If it logs in successfully then I want it to run the original page requested with the new logged in context. If it fails to log in then it will redirect to a custom error page.

My question is where would I need to insert this logic into the site?

I have seen some suggestions on subclassing the Authorize attribute and overriding some of the methods but I'm not 100% sure how to go about this (eg what I would override and what I'd do in those overridden methods.

I've also had a look at putting the logic at a controller level but I am led to understand that the authorize attribute would redirect it away from the controller before any code in the controller itself was run.

Was it helpful?

Solution

It would be better to write a custom authorization attribute that will entirely replace the default functionality and check for the query string parameter and if present, decrypt it and authenticate the user. If you are using FormsAuthentication that would be to call the FormsAuthentication.SetAuthCookie method. Something along the lines of:

public class TokenAuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        string token = filterContext.HttpContext.Request["token"];
        IPrincipal user = this.GetUserFromToken(token);
        if (user == null)
        {
            this.HandleUnAuthorizedRequest(filterContext);
        }
        else
        {
            FormsAuthentication.SetAuthCookie(user.Identity.Name, false);
            filterContext.HttpContext.User = user;
        }
    }

    private IPrincipal GetUserFromToken(string token)
    {
        // Here you could put your custom logic to decrypt the token and
        // extract the associated user from it
        throw new NotImplementedException();
    }

    private void HandleUnAuthorizedRequest(AuthorizationContext filterContext)
    {
        filterContext.Result = new ViewResult
        {
            ViewName = "~/Views/Shared/CustomError.cshtml",
        };
    }
}

and then you could decorate your action with this attribute:

[TokenAuthorize]
public ActionResult ProcessEmail(string returnUrl)
{
    if (Url.IsLocalUrl(returnUrl))
    {
        return Redirect(returnUrl);
    }

    return RedirectToAction("Index", "Home");
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top