Reputation: 81
I've built a RESTful API (using ASP.NET Web API 2) which is only meant to be consumed from a single end-point. This end-point is a basic front-end site containing only HTML/CSS/JS. Due to various reasons, the front-end site and the API are completely external from one-another, with the front-end site being whitelisted in the API's CORS configuration.
I'm now trying to lock-down the API so that it's only accessible from this particular end-point, without introducing a new login system, because the context of where this page lives ensures that anyone accessing it is already a trusted user (it's technically behind a login system, but the page consuming the API has almost no knowledge of this context).
At a high level, I'd like to introduce a statically defined API Key of some sort, that would be hardcoded into both the API and the JavaScript of the consuming page, to help ensure that it's the only end-point accessing the API. We can assume that all communications between the front-end page and the API will be over a secure SSL/TLS connection.
My question: for such a case where I want to authenticate API requests from a particular page with a statically-defined API Key, what would be my best option from an ease-of-implementation standpoint? Most of the articles that I've found on Web API Authorization pivot around a user login system and seem grossly over-engineered for my particular use-case. I'd consider myself a novice when it comes to the subject and so I'm really just hoping for someone to point me in the right direction.
Thanks!
Upvotes: 4
Views: 2612
Reputation: 764
It seems like you are looking for a global filter in this specific case.
An authentication filter is a component that authenticates an HTTP request
You would basically send the shared / static api key with every request in the Authorization header and the custom filter would process this and decide whether the request is valid or not.
A basic implementation of the filter:
public class ApiKeyAuthenticationAttribute : IAuthenticationFilter
{
public bool AllowMultiple { get; set; }
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
HttpRequestMessage request = context.Request;
// Get Auth header
AuthenticationHeaderValue authorization = request.Headers.Authorization;
// Validate the static token
if (authorization?.Parameter == "123")
{
IPrincipal principal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim> { new Claim("CLAIMTYPE", "CLAIMVALUE") }));
context.Principal = principal;
}
else
{
context.ErrorResult = new AuthenticationFailureResult(request);
}
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
var challenge = new AuthenticationHeaderValue("Basic");
context.Result = new AddChallengeOnUnauthorizedResult(challenge, context.Result);
return Task.FromResult(0);
}
}
And to enable it for all calls to your api add it to your WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Some more config here
config.Filters.Add(new IdentityBasicAuthenticationAttribute());
}
}
The AuthenticationFailureResult and AddChallengeOnUnauthorizedResult are implementations of IHttpActionResult. For comprehensiveness I will add them here.
AuthenticationFailureResult
class AuthenticationFailureResult : IHttpActionResult
{
private HttpRequestMessage _request;
public AuthenticationFailureResult(HttpRequestMessage request)
{
_request = request;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
response.RequestMessage = _request;
response.Content = new StringContent("ACCESS DENIED MESSAGE");
return Task.FromResult(response);
}
}
AddChallengeOnUnauthorizedResult
class AddChallengeOnUnauthorizedResult : IHttpActionResult
{
public AddChallengeOnUnauthorizedResult(AuthenticationHeaderValue challenge, IHttpActionResult innerResult)
{
Challenge = challenge;
InnerResult = innerResult;
}
public AuthenticationHeaderValue Challenge { get; private set; }
public IHttpActionResult InnerResult { get; private set; }
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage response = await InnerResult.ExecuteAsync(cancellationToken);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
// Only add one challenge per authentication scheme.
if (!response.Headers.WwwAuthenticate.Any((h) => h.Scheme == Challenge.Scheme))
{
response.Headers.WwwAuthenticate.Add(Challenge);
}
}
return response;
}
}
This code is from or a derivative of this article Authentication Filters in ASP.NET Web API 2 and this article Authentication Filters in ASP.NET Web API 2
Upvotes: 4