Reputation: 85
I created a ASP.NET Core Web API and React in one web application and deployed to production.
The end-points are:
www.myserver.com/obs is the front-end app.
www.myserver.com/obs/api/GetValue is the web API.
How do you secure the Web API endpoints so that only requests from the react application is able to call the API?
For example, if I were to do a Postman call on a remote machine to www.myserver.com/obs/api/GetValue it should not return the resource.
One way is to use an API Key however where would you put the API-Key on the react side? I read that you can put it in .env file however in production you can still find the file using dev-tools.
Another option I read is to create a proxy API that the react app calls and the proxy has the API Key but that seems to be overkill, is there a simpler way that I have missed?
Upvotes: 2
Views: 2003
Reputation: 493
You can't. Your react app is readable by the browser, and therefore readable by anyone who knows how to use browser developer tools or intercept HTTP(s) requests on their computer. If your react app can talk to your API, so can anyone else. The same goes for a proxy. You can find a more detailed answer here.
If you want to control access you could introduce authentication, and only grant access to trusted users, but you still can't stop them from accessing your API outside of your react app if they really wanted to.
There are steps you can take to make it more difficult. I would recommend that you read up on creating secure APIs. Some links to get you started:
Upvotes: 2
Reputation: 21363
One way is to use an API Key however where would you put the API-Key on the react side?
Yes, you could create an API Key Middleware and use it to authenticate the request. If the request is from the react application, you could add the API key in the request header. Code like this:
Using fetch method:
fetch('/api/MoviesAPI', {
method: 'Get', // or 'Post'
headers: {
'Content-Type': 'application/json',
'ApiKey':'Test-value',
},
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
})
.catch((error) => {
console.log('Error:', error);
});
Using Ajax method:
$.ajax({
type: "Get",
url: "/api/MoviesAPI", //remember change the controller to your owns.
contentType: 'application/json',
beforeSend: function (xhr) { xhr.setRequestHeader('ApiKey', 'test-value'); },
success: function (data) {
console.log(data)
},
failure: function (response) {
console.log(response.responseText);
},
error: function (response) {
console.log(response.responseText);
}
});
More detail information about sending request with custom header in reactjs, you can search "reactjs call api with custom headers" using Google or Bing, there have lots of articles related it.
Besides, about creating an API key Middleware, you can refer the following steps:
create an ApiKeyMiddleware.cs class in the API application, and add the following code:
public class ApiKeyMiddleware
{
public ApiKeyMiddleware(RequestDelegate next, IConfiguration configuration)
{
_next = next;
_configuration = configuration;
}
private readonly RequestDelegate _next;
private readonly IConfiguration _configuration;
public async Task Invoke(HttpContext context)
{
if (context.Request.Path.StartsWithSegments(new PathString("/api")))
{
//Let's check if this is an API Call
if (context.Request.Headers.Keys.Contains("ApiKey", StringComparer.InvariantCultureIgnoreCase))
{
// validate the supplied API key
// Validate it
var headerKey = context.Request.Headers["ApiKey"].FirstOrDefault();
await ValidateApiKey(context, _next, headerKey);
}
else
{
await _next.Invoke(context);
}
}
else
{
await _next.Invoke(context);
}
}
private async Task ValidateApiKey(HttpContext context, RequestDelegate next, string key)
{
// validate it here
var valid = false;
var Apikey = _configuration["ApiKey"];
if (key != null && key==Apikey)
{
valid = true;
}
if (!valid)
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
await context.Response.WriteAsync("Invalid API Key");
}
else
{
var identity = new GenericIdentity("API");
var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" });
context.User = principal;
await next.Invoke(context);
}
}
}
Register this Middleware in the Configure method in the Startup.cs file.
app.UseMiddleware<ApiKeyMiddleware>(); //add APIkeyMiddleware
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication(); //Call the UseAuthentication
app.UseAuthorization();
In the API controller or action method, add Authorize
attribute.
[HttpGet]
[Authorize]
public async Task<ActionResult> GetMovie()
{
return Ok(await _context.Movie.ToListAsync());
}
Then, if the request header doesn't contain the ApiKey or the key value is invalid, it will not return the resource.
Edit:
About the API key value, you could store them in the appsettings.json file or In memory .NET objects. When using it you could get it from the Configuration.
For example: store it in the appsettings.json file:
{
...
"Apikey": "my Test API key"
}
Then, using the following code to get the key value
public ApiKeyMiddleware(RequestDelegate next, IConfiguration configuration)
{
_next = next;
_configuration = configuration;
}
private readonly RequestDelegate _next;
private readonly IConfiguration _configuration;
private async Task ValidateApiKey(HttpContext context, RequestDelegate next, string key)
{
// validate it here
var valid = false;
//get the key value from configuration.
var Apikey = _configuration["ApiKey"];
...
On the react side, you could create a service to get this key value, then send a request with the api key.
Upvotes: 2