How to use the Api key in Web Api to authenticate a service using forms authentication

I am using MVC 4 Web Api and I want users to be authenticated before using my service.

I executed an authorization message handler that works fine.

public class AuthorizationHandler : DelegatingHandler { private readonly AuthenticationService _authenticationService = new AuthenticationService(); protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { IEnumerable<string> apiKeyHeaderValues = null; if (request.Headers.TryGetValues("X-ApiKey", out apiKeyHeaderValues)) { var apiKeyHeaderValue = apiKeyHeaderValues.First(); // ... your authentication logic here ... var user = _authenticationService.GetUserByKey(new Guid(apiKeyHeaderValue)); if (user != null) { var userId = user.Id; var userIdClaim = new Claim(ClaimTypes.SerialNumber, userId.ToString()); var identity = new ClaimsIdentity(new[] { userIdClaim }, "ApiKey"); var principal = new ClaimsPrincipal(identity); Thread.CurrentPrincipal = principal; } } return base.SendAsync(request, cancellationToken); } } 

The problem is that I am using forms authentication.

 [HttpPost] public ActionResult Login(UserModel model) { if (ModelState.IsValid) { var user = _authenticationService.Login(model); if (user != null) { // Add the api key to the HttpResponse??? } return View(model); } return View(model); } 

When I call my api:

  [Authorize] public class TestController : ApiController { public string GetLists() { return "Weee"; } } 

The handler cannot find the X-ApiKey header.

Is there a way to add the user api key to the HTTP response header and save the key there if the user is logged in? Is there any other way to implement this functionality?

+7
source share
2 answers

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.

+2
source

From your code samples, it doesn't seem like you're using web forms. Can you use forms-based authentication? Do you use a member provider inside your service to verify user credentials?

You can use the HttpClient class and possibly its DefaultRequestHeaders or HttpRequestMessage property from the code that will call the API to set the headers.

Here are some examples of HttpClient: http://www.asp.net/web-api/overview/web-api-clients/calling-a-web-api-from-a-net-client

+1
source

All Articles