Adapting hypermedia links based on environment - azure

I have an on-premise ASP.NET Web API that's querying on-premise data. The plan is for the client web application to call an Azure Function which will deal with authentication (Azure AD B2C) and validating the request before actually forwarding the request to the on-premise API itself (over a VPN).
The API is generating Hypermedia links that point to the API itself. This works nicely when querying the API directly as each of the links helps in the discovery of the application.
This API is currently in use locally within the organisation, but we now need to expose it so it can be consumed over the web. We don't want to expose the API directly, we'd rather route it through a Function App that can authenticate, validate and perform any other logic we may need.
The question I have is, how would you translate these URLs to an endpoint in the Azure Function? i.e., I would really like the consuming web application to be able to use these Hypermedia links directly, and have the Azure Function route them to the correct API endpoint.
In an ideal world, we'd have the links exposed on the client, which would map to the resource. As the API isn't exposed, how do we route it via the Function App?

It sounds like what you want is for Azure Functions to operate as a reverse proxy.
One trick to achieve this is to have one HttpTrigger that catches all traffic that hits your function application. You can do this by setting the properties route: "{*uri}" and methods: ["get", "post", "put", "patch", "delete"] in the function.json. You can add additional HTTP methods to the methods list as necessary. These should catch all requests in the form "https://{app-name}.azurefunctions.net/api/*".
The code below is a rough outline of how you could achieve the redirect from your function app to the unexposed API. In it's current representation, the relative URI path after /api/ will be redirected to your unexposed api with the exact same body request.
using System.Net;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
//Validation logic here
string actualUrl = "{hosturl}/";
string proxyUrl = "https://{app-name}.azurewebsites.net/api/";
req.RequestUri = new Uri(req.RequestUri.ToString().Replace(proxyUrl, actualUrl));
req.Headers.Host = req.RequestUri.Host;
using(var client = new HttpClient())
{
return await client.SendAsync(req);
}
}
You can now generate all of your Hypermedia links pointing to your function hostname, and they will be properly redirected.
NOTE: If you expect many instances of the function to spin up (i.e. a high concurrent usage of your function), then it is possible that you will run into SocketException errors as documented here.

Related

Best ways to secure access to Azure functions

I have 2 types of Azure functions:
I have a front-facing function (Python) that I expect it to only be reachable from another Azure service (Azure Workbooks) via HTTP
I have functions (HTTP triggers, Python & C#) that should be only reached by another function (which is a time trigger one and in Python).
I've been reading on Function access keys but I'm not fully understanding how to manage access levels between functions.
Or should this be handled by permissions?
In short, what are the best practices concerning :
a) Communication between Azure functions?
b) Communication between Azure services <-> Functions
Communication between functions should happen by Message Queue IMHO. Network is not reliable on cloud.
About the other function, you should set authorization level to function and rotate the key once in a while.
[FunctionName("Function1")]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = "products/{category:alpha}/{id:int?}")] HttpRequest req,
string category, int? id, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
var message = String.Format($"Category: {category}, ID: {id}");
return (ActionResult)new OkObjectResult(message);
}
https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook-trigger?tabs=in-process%2Cfunctionsv2&pivots=programming-language-csharp#authorization-scopes-function-level
Another good tip would be place an Azure API Management in front and require Key on that too.

Calling WebApi from Azure function securely

I have a scenario where I have asp.net core web api and a azure function. I call web api from azure time triggered function for every 1 hour. I do not have authentication enabled on the web api and I do not want public to access it. Only my azure function should be able to access the web api. How can I restrict web api to access from public but only from azure function with out implementing authentication.
I tried the below,
In webapi appsettings file, I updated "AllowedHosts":"https://testfuntionapp.azurewebsites.net". My testfuntionapp is unable to access web api with this change.
I am trying for a cost effective solution.
Please check if my findings help to:
How can I restrict web api to access from public but only from azure function without implementing authentication.
This is where the Azure Virtual Networks comes to.
Create the Virtual Network, configure the Azure Function to only be callable on this VNet and also can configure your core app access to the VNet.
By using Private Endpoints, resources are accessible only via your Virtual Network.
If Virtual Network Integration enabled, then the Azure Function is able to access the designated resource via the configured private endpoints, which is a higher level of security.
References:
Michael S Collier's article on Azure Functions with Private Endpoints.
Access an App Service integrated with a Virtual Network from other Azure resources like Azure Functions
I would also recommend private endpoints as suggested by HariKrishnaRajoli.
In theory it would also suffice to just configure http header filtering for access restriction rules and only allow requests containing a secret header known only to your azure function code. This is probably comparable to "basic" HTTP authentication security, and weaker than the other options.
As an alternative, you can use AAD authentication without really "implementing" much authentication code.
Configure Easy Auth on the WebApi
Configure ManagedIdentity for your Azure Function
Give your the Managed Identity access to call the WebApi using "App Role Assignments"
Extend your Azure Function to acquire and pass access token when calling the WebApi
If you only want your Azure function to be able to access your web API, you can restrict access to your web API by IP address.
In your web API's appsettings.json file, add the following:
"AllowedHosts": "https://testfuntionapp.azurewebsites.net"
This will restrict access to your web API to only requests coming from the specified Azure function.
The fastest and "dirtiest" way to do this, is to pass an API key as a header to your REST API from the function.
You can hardcode the API key to your config file or load it from your DB.
My implementation:
namespace CoreApi.Middleware
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class RequirePartnerApiKeyAttribute : Attribute, IAsyncActionFilter
{
private const string ApiKeyHeaderName = "x-partner-apikey";
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
//----------------------------------------------------
// Validate Api Key
//----------------------------------------------------
if (!context.HttpContext.Request.Headers.TryGetValue(ApiKeyHeaderName, out var apiKey) ||
string.IsNullOrEmpty(apiKey))
{
context.Result = new UnauthorizedResult();
return;
}
var partnerService = context.HttpContext.RequestServices.GetRequiredService<IPartnerService>();
var apiRequest = new PartnerAuthenticateRequest { PartnerApiKey = apiKey };
var partner = partnerService.Authenticate(apiRequest, new CancellationToken()).Result;
if (partner == null)
{
context.Result = new UnauthorizedResult();
return;
}
else
{
if (Enum.IsDefined(typeof(ApiKeyTypes), partner.PartnerName))
context.HttpContext.Items["APIKeyName"] = Enum.Parse(typeof(ApiKeyTypes), partner.PartnerName);
}
//----------------------------------------------------
await next();
}
}
}
You can access the API key from any middleware by calling: context.HttpContext.Items["APIKeyName"]

Best practices for using Azure CDN in scenarios that require authentication to access?

I've never configured a CDN before and I'm looking for guidance for how to approach the configuration for my scenario.
For context, I'm currently using Azure Verizon Premium CDN and an Azure App service (.Net Core). My authentication platform is Azure Active Directory.
I have a static web app that has lots of static content (hundreds of files). Users hit the website at (www.mysite.azurewebsites.net/index.html). Index.html then fetches large amounts of static content (various, images, video, metadata). Initially, the requirement was to just serve the content publicly but now I have requirements around restricting access to some the content based on whether or not a user has a certain role & the user hits a certain path. I also need to be able to keep some content public and highly available to end users.
Before authorization requirements came in, I was able to successfully set up a CDN endpoint (www.mysite-cdn.azureedge.net) and point it to my app service no problem! I can hit the site and see the site content from the cdn endpoint without auth no issue.
Problems came when I added authentication to my site my CDN is redirected during the authentication flow back to the origin endpoint. The origin authentication middleware (Nuget: Microsoft.Identity.Web) automatically assembles the callback uri for AAD as "www.mysite.azurewebsites.net/signin-oidc". So the present flow for a user hitting the CDN endpoint returns them to an error page on the app service endpoint. Although if they manually redirect to "www.mysite.azurewebsites.net" they are logged in, I don't want to redirect the user back to origin, I want to keep them on www.mysite-cdn.azurewebsites.net.
Essentially, I want to be able to enable these flows:
Public End User -> CDN Endpoint + Public path -> (CDN request origin and caches) -> End user sees site at CDN endpoint
Internal End User -> CDN Endpoint + Private path -> (CDN request origin and has access) -> User is permitted into site at CDN endpoint
Internal End User -> CDN Endpoint + Private path -> (CDN request origin and DOESN’T have access) -> User is redirected to public CDN endpoint (or unauthorized page)
This is the auth check for each static file in OnPrepareResponse for static file options. This checks authentication before requesting a static asset in this folder on the server. This works fine without the CDN. It should be noted that I also do role checks and this has been simplified for the sake of the example as it repos with Authentication.
OnPrepareResponse = staticContext =>
{
// require authentication
if (authenticate &&
!staticContext.Context.User.Identity.IsAuthenticated)
{
// Redirect back to index sign in page all unauthorized requests
staticContext.Context.Response.Redirect(unauthenticatedRedirectPath);
}
},
I did find this stack overflow which seemed similar to my problem however I am using a different NuGet package (Microsoft.Identity.Web). I implemented a check to redirect however that did not seem to work and cause an infinite loop when trying to login.
Action<MicrosoftIdentityOptions> _configureMsOptions = (options) =>
{
options.Instance = Configuration["AzureAd:Instance"];
options.Domain = Configuration["AzureAd:Domain"];
options.ClientId = Configuration["AzureAd:ClientId"];
options.TenantId = Configuration["AzureAd:TenantId"];
options.CallbackPath = Configuration["AzureAd:CallbackPath"];
options.Events.OnRedirectToIdentityProvider = async (context) =>
{
// This check doesn’t work because the request host always is mysite.azurewebsites.net in this context
// if (context.Request.Host.Value.Contains("mysite-cdn"))
// {
context.ProtocolMessage.RedirectUri = "https://" + "mysite-cdn-dev.azureedge.net/" + Configuration["AzureAd:CallbackPath"];
//}
};
};
I've started looking into Azure Front door, as that seems to be more applicable to my use case but haven't set it up. It provides some level of caching/POP as well as security. It looks like it's also possible to use with Azure AD with some web server tweaking. It would be good to know from others if Azure Front Door sounds like a more sensible CDN solution approach vs Azure CDN.
I've also looked into Azure CDN Token authentication- but that seems to be something that also requires me to stamp each request with an Auth token. It changes my architecture such that I can no longer just 'point' my cdn at the web app and instead my app would give the browser a token so it could securely request each asset.
All that said, I'm looking for guidance on how best to configure an application using a CDN while also using authentication. Thanks!

How to ensure user identity in Azure Function Call? ASP NET Core Web App

I am building an ASP NET Core web application that will perform a delete operation for a user on a remote datastore. Currently, I have a frontend web app that communicates with Azure Functions to delete a user. I have a concern with security. How can I make sure the user is the logged-in user? Currently, in my ASP Net Core Web App, I have the user authenticate with AAD using Microsoft as an Identity Provider. From a client-side, the user is verified and taken care of. However, my app calls the Azure Functions endpoints. Is there an extra level of security that I could add. I only want to give my web app (registered in Azure) the ability to call the endpoints.
There are two main ways to authenticate your users. One is to use the App Services authentication options: https://learn.microsoft.com/en-us/azure/app-service/configure-authentication-provider-aad. This is by far the easier method.
If you need more control over things than what is available in the service, then you can implement validation within the Function itself as Thiago mentioned. Ben Morris did a fairly detailed post on how to implement your own OAUTH provider inside your Function App with C#: https://www.ben-morris.com/custom-token-authentication-in-azure-functions-using-bindings/ If you can take care of the validation in the Function's DI container, validation inside a specific function is fairly clean as seen in the post:
public class ExampleHttpFunction
{
private readonly IAccessTokenProvider _tokenProvider;
public ExampleHttpFunction(IAccessTokenProvider tokenProvider)
{
_tokenProvider = tokenProvider;
}
[FunctionName("ExampleHttpFunction")]
public IActionResult Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "example")] HttpRequest req, ILogger log)
{
var result = _tokenProvider.ValidateToken(req);
if (result.Status == AccessTokenStatus.Valid)
{
log.LogInformation($"Request received for {result.Principal.Identity.Name}.");
return new OkResult();
}
else
{
return new UnauthorizedResult();
}
}
}

How to customize remote api call?

I write a signup webpage with nodejs, and in this webpage, I use ajax to call the function signup like this
$.ajax({
method: "POST",
url: "/signup",
data: { tel: tel, password: password}
})
And in app.js, the signup function like this
.post('/signup', async (ctx) => {
//do something
})
And now everyone can call the signup function with the url http://domain/signup without visiting the signup webpage, I think it's a mistake, I only want the local program can call this function, how can I fix this?
Typically it's either API Keys for doling out general access, or IP-based restrictions at either the application or network level.
API Keys are a token that identifies and authenticates an endpoint. You can also use it to track usage and/or ban abuse. For example, see Google Maps' documentation about using their API. Then all API calls have that key:
https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap
This allows the server to parse the key, check against it's key database or whatever, and allow access. You'll need to use HTTPS for this if it's over any public network.
IP or other network restrictions are easier to setup and best when you have a 1:1 relationship with your API. That is, your application alone accesses this API, and you control all the servers, etc.

Resources