Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Api key in Web Api for service authentication using forms authentication

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

I have implemented an authorization message handler, that works just 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 use 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 can not find the X-ApiKey header.

Is there a way to add the user's api key to the http response header and to keep the key there, as long as the user is logged in? Is there another way to implement this functionality?

like image 920
Ivan Stoyanov Avatar asked Apr 22 '13 22:04

Ivan Stoyanov


People also ask

How does web API implement form authentication?

Forms authentication uses an HTML form to send the user's credentials to the server. It is not an Internet standard. Forms authentication is only appropriate for web APIs that are called from a web application, so that the user can interact with the HTML form. - Easy to implement: Built into ASP.NET.

Which three methods can be used to authenticate to an API?

We'll highlight three major methods of adding security to an API — HTTP Basic Auth, API Keys, and OAuth. We'll identify the pros and cons of each approach to authentication, and finally recommend the best way for most providers to leverage this power.

How do I authenticate using API?

There are three ways to authenticate with this API: with an OAuth2 Access Token in the Authorization request header field (which uses the Bearer authentication scheme to transmit the Access Token) with your Client ID and Client Secret credentials. only with your Client ID.

What type of authentication is used in web API?

There are three ways to authenticate users when calling a web API: API key authentication. Basic authentication. Session-based authentication.

How to create ASP NET Web API with key based authentication?

Steps to create asp.net web api with key based authentication 1 Create a new asp.net web application project. 2 Select Empty asp.net web application and choose MVC and Web API core references. 3 Create a folder 'MessageAPIHandler' and add class file for custom authorization handlaer as 'AuthorizationHandler.cs'. ... More items...

What are the most used authentication methods in REST APIs?

An API might authenticate you but not authorize you to make a certain request. Now that we know what authentication is, let's see what are the most used authentication methods in REST APIs. Let's review the 4 most used authentication methods used today. 1. HTTP Authentication Schemes (Basic & Bearer)

Why do I need API keys for my application?

Applications that provide first class APIs require more than simple authentication, they require api keys. API keys generated by your users allow their programmatic services to interact with your apis. These must be generated in a safe way and have different requirements than UI tokens.

How to add an API key to your website?

You will need to add an API key to each request so that the API can identify you. In order to get an API key, you need to somehow register with the API server and enter your identity data. On the example of RapidAPI – you can choose the method of registration that will be convenient for you.


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 Login action result:

[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 it I am using the CookieHelper, that I created. It consists of an interface:

public interface ICookieHelper
{
    void SetCookie(User user);

    void RemoveCookie();

    Guid GetUserId();
}

And a 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;
    }
}

By having 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. He can not access my api and recieves a 401 error. Also I can retrieve the api key, which I use as a user ID, anywhere in my code, which makes it very clean and readable.

I do not think that using cookies is the best solution, as some user may have disabled them in their browser, but at the moment I have not found a better way to do the authorization.

like image 186
Ivan Stoyanov Avatar answered Sep 30 '22 06:09

Ivan Stoyanov


From your code samples it doesn't seem like you're using Web Forms. Might you be using Forms Authentication? Are you using the Membership Provider inside your service to validate user credentials?

You can use the HttpClient class and maybe its property DefaultRequestHeaders or an HttpRequestMessage from the code that will be calling the API to set the headers.

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

like image 37
Ezequiel Avatar answered Sep 30 '22 06:09

Ezequiel