Rama
Rama

Reputation: 73

allow mvc5 c# webapi so that only my app can access it

I am making the api calls through the controller action method as below. The following is the working code of it. But I want to secure the webapi so that only my application can access it. I have seen sources with login credentials but in my case it is a public facing website with no login users. Only the calls from my application should access it. Could anyone please suggest what can be done. or Is my current code with ValidateReferrer is suffice to handle?

[HttpGet]
[ValidateReferrer]
[ActionName("GetFind")]
[CacheOutput(ClientTimeSpan = 300, ServerTimeSpan = 300)]
public ApiQueryResponse GetFind(string query)
{
    return _Worker.GetFind(query);
}

Validate Referrer in the controller has the following implementation:

public override void OnAuthorization(AuthorizationContext filterContext)
{
    if (filterContext.HttpContext == null)
    {
        throw new System.Web.HttpException("No Http context, request not allowed.");
    }
    else
    {
        if (filterContext.HttpContext.Request.UrlReferrer == null)
        {
            throw new System.Web.HttpException("Referrer information missing, request not allowed.");
        }
        else if (filterContext.HttpContext.Request.UrlReferrer.Host != filterContext.HttpContext.Request.Url.Host)
        {
            throw new System.Web.HttpException(string.Format("Possible cross site request forgery attack, request sent from another site: {0}", filterContext.HttpContext.Request.UrlReferrer.Host));
        }
    }
}

In the worker class,

public ApiQueryResponse GetFind(string query)
{
    var results = GetResults(Settings.ApiKey, SetFindParameters(query), Resource);           
    return results;
}

private ApiQueryResponse GetResults(string apiKey, string parameterQuery, string Resource)
{
    var results = new ApiQueryResponse();
    if (apiKey != null && !String.IsNullOrWhiteSpace(apiKey))
    {
        using (var client = new HttpClient())
        {
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            var response = client.GetAsync(string.Format("{0}/{1}?{2}&key={3}", WebApiUrl, Resource, parameterQuery, apiKey)).Result;
            if (response.IsSuccessStatusCode)
            {
                var responseBodyAsText = response.Content.ReadAsStringAsync().Result;
                results = JsonConvert.DeserializeObject<ApiQueryResponse>(responseBodyAsText);
            }
        }
    }
    return results;
}

Upvotes: 0

Views: 1406

Answers (1)

Developer
Developer

Reputation: 6440

Again this is the case where you have to authenticate your "application" but not users. If you check facebook/twitter/gmail api's, they have a client secret and client id to authenticate the application. But still there will be an "Authorize" call made with this id and secret for which the api returns a token and this token is used henceforth to authorize the other requests. This token will also have an expiry and there are methods to get refresh tokens.

Thus said, you have to take a call on how much security you have to implement for your api's. You could have a similar approach where your client first asks for a security token by providing the client id and secret (which should really be a secret). You can check this id and secret against your store (may be database) and if that passes the validation, you can send back a token which you could authroize using [Authroize] attribute or by custom validation.

How to create tokens should be another discussion IMO. Simple approach is mentioned here for eg- how to generate a unique token which expires after 24 hours?. There are other standard ways of generating tokens JWT/OAuth tokens.

EDIT As a simple approach (not taking much security aspects into consideration) would be:

  • Create an app secret (may be a Guid value)
  • While sending request, take current timestamp and encrypt (have your own encrypt and decrypt logic) the timestamp with the app secret. Lets call that encrypted value as 'token'
  • Pass the token in your request header (may be a custom header,say, x-my-auth)
  • In the api, have a custom authorize filter
  • In the custom filter, overridden OnAuthroizeCore method, get the token from request header
  • Decrypt the token using the same app secret and you will get the timestamp sent from the client
  • If decryption is fine, then we are through the first step/ or the token passed the first step
  • Additionaly, check whether the difference between the current time and the time decrypted from token is more than 5(*you can have your own expiry value)
  • If the difference is more than your expiry limit, return false which would throw unauthorized exception back to the client (do the same if the token fails to decrypt)
  • The expiry check is to handle the scenario where someone hacking your token from the request and then using it afterwards. In case if he uses the token after your expiry, this would throw unauthorized

Consider the above logic and entire description just as a "food for thought" and DO NOT use it without proper research and understanding. My idea was to give some basic idea about the application authentication until someone really good at this writes up a really nice article in this post

Upvotes: 3

Related Questions