I found the following article http://www.asp.net/web-api/overview/working-with-http/http-cookies Using it, I configured my AuthorizationHandler to use cookies:
public class AuthorizationHandler : DelegatingHandler { private readonly IAuthenticationService _authenticationService = new AuthenticationService(); protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var cookie = request.Headers.GetCookies(Constants.ApiKey).FirstOrDefault(); if (cookie != null) { var apiKey = cookie[Constants.ApiKey].Value; try { var guidKey = Guid.Parse(apiKey); var user = _authenticationService.GetUserByKey(guidKey); if (user != null) { var userIdClaim = new Claim(ClaimTypes.Name, apiKey); var identity = new ClaimsIdentity(new[] { userIdClaim }, "ApiKey"); var principal = new ClaimsPrincipal(identity); Thread.CurrentPrincipal = principal; } } catch (FormatException) { } } return base.SendAsync(request, cancellationToken); } }
I configured my result of the login action:
[HttpPost] public ActionResult Login(LoginModel model) { if (ModelState.IsValid) { var user = _authenticationService.Login(model); if (user != null) { _cookieHelper.SetCookie(user); return RedirectToAction("Index", "Home"); } ModelState.AddModelError("", "Incorrect username or password"); return View(model); } return View(model); }
Inside, I use the CookieHelper that I created. It consists of an interface:
public interface ICookieHelper { void SetCookie(User user); void RemoveCookie(); Guid GetUserId(); }
And the class that implements the interface:
public class CookieHelper : ICookieHelper { private readonly HttpContextBase _httpContext; public CookieHelper(HttpContextBase httpContext) { _httpContext = httpContext; } public void SetCookie(User user) { var cookie = new HttpCookie(Constants.ApiKey, user.UserId.ToString()) { Expires = DateTime.UtcNow.AddDays(1) }; _httpContext.Response.Cookies.Add(cookie); } public void RemoveCookie() { var cookie = _httpContext.Response.Cookies[Constants.ApiKey]; if (cookie != null) { cookie.Expires = DateTime.UtcNow.AddDays(-1); _httpContext.Response.Cookies.Add(cookie); } } public Guid GetUserId() { var cookie = _httpContext.Request.Cookies[Constants.ApiKey]; if (cookie != null && cookie.Value != null) { return Guid.Parse(cookie.Value); } return Guid.Empty; } }
With this configuration, now I can use the Authorize attribute for my ApiControllers:
[Authorize] public class TestController : ApiController { public string Get() { return String.Empty; } }
This means that if the user is not logged in. It cannot access my api and gets error 401. I can also get the api key, which I use as the user ID anywhere in my code, which makes it very clean and readable.
I do not think that the use of cookies is the best solution, as some users can disable them in their browser, but at the moment I have not found a better way to authorize.