Isolated Azure Function bypass authentication middleware for Swagger UI page - azure

I've created a new .NET 6 Isolated Azure Function.
I've added the specific Nuget package for Swagger/OpenAPI (for Isolated):
Microsoft.Azure.Functions.Worker.Extensions.OpenApi v1.3.0
I then decorate my HttpTrigger Function with the appropriate [OpenApiOperation] attribute and correctly add the ConfigureOpenApi() method inside my Program.cs like so:
using Microsoft.Azure.Functions.Worker.Extensions.OpenApi.Extensions;
using Microsoft.Extensions.Hosting;
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureOpenApi()
.Build();
host.Run();
Everything works as expected and I can access my swagger UI url:
http://localhost:7001/api/swagger/ui
It's now time to protect my Az Function and add support for JWT token validation.
To do so, I followed this great article:
https://joonasw.net/view/azure-ad-jwt-authentication-in-net-isolated-process-azure-functions
Because I'm inside an Isolated Azure Function I can now leverage middleware and the article does just that. It creates two middleware. So I've added them like so:
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults((context, builder) =>
{
builder.UseMiddleware<AuthenticationMiddleware>();
builder.UseMiddleware<AuthorizationMiddleware>();
})
.ConfigureOpenApi()
.ConfigureServices((context, services) =>
{
...
})
.Build();
host.Run();
QUESTION:
Whenever I launch my Azure Function and I try to access my swagger UI url http://localhost:7001/api/swagger/ui
I am now getting a 401 unauthorized since the Authentication Middleware kicks in and since I don't have an authorization header value.
I believe it is normal that I don't have an authorization header value since I'm only trying to look at a swagger UI url.
How can I ask the Authentication middleware to not look for an authorization header if I'm coming from an url that has the word "swagger" in it?
Or is there a better/different way to achieve this?
Thanks
Sincerely

Looking at the documentation, you can use the UseWhen extension method.
you will need to use at least the version 1.8.0-preview1 of the Microsoft.Azure.Functions.Worker nuget package. (See github issue)
Then in you code you should be able to do something like that:
using Microsoft.Azure.Functions.Worker.Extensions.OpenApi.Extensions;
using Microsoft.Extensions.Hosting;
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults((hostContext, builder) =>
{
builder.UseWhen<AuthenticationMiddleware>(functionContext =>
{
// Only use the middleware if not related to swagger
// Condition could be improved tho.
return !functionContext.FunctionDefinition.Name.Contains("Swagger");
});
...
})
...
host.Run();

Related

How can I configure an Azure Function to work with dynamic configuration using Azure App Configuration?

I'm trying to write an Azure Function that works as a multi-tenant. When a clientId comes from the clients as a parameter, I need to fetch its configuration from Azure App Configuration using labels.
Let me explain in a pseudo code;
I'm using .Net 6 Azure Function Isolated.
I made configuration in Program.cs like in the below code;
new HostBuilder()
.ConfigureAppConfiguration(builder =>
{
string cs = "connection-string"; //which is actually comes from environment veriable
builder.AddAzureAppConfiguration(options =>
{
options.Connect(cs);
//options.Select(KeyFilter.Any, Environment.GetEnvironmentVariable("ClientId")); //It works that way, but I don't want to do it. I need to read the clientId while I receive the request
});
})
Let's assume that we made a middleware and a request came out
public RequestMiddleware(){
//Here I need to inject some service
}
//Invoke method of middleware
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next){
HttpRequestData requestData = await context.GetHttpRequestDataAsync();
requestData.Headers.TryGetValues("client-id", out var clientId);
var id = clientId.First();
//And here, I need to configure App Configuration to use the id as a label. As I did in the Program.cs
//like this ; options.Select(KeyFilter.Any, id);
}
Does it possible to load configuration by the label in the request lifecycle? Please keep in mind that Azure App Configuration in the standard tier has a limitation on requests, 3000 per hour. That's why it won't be a good idea to load AppConfiguration each time.
What you can do is work with a static Dictionary to 'cache' and whenever a request with some new client id arrives, you fetch the configuration add it to the dictionary for the future requests.
This document discussed strategies you should think about when using App Configuration in multi-tenant applications.
https://learn.microsoft.com/en-us/azure/architecture/guide/multitenant/service/app-configuration
From your description, it looks like you want to load tenant-specific config on-demand instead of all at once at the app startup, so you shouldn't need the code in program.cs. I would recommend using prefixes instead of labels to differentiate the config date for each tenant.
Check out the application-side caching section to learn more what options you have. I imagine, in the middleware, upon every request, you will first check if you have a cached IConfiguration instance for the tenant; if not, you will load the config for this tenant and cache it; if yes, you serve the request based on the config in your cache.

How do I properly proxy a request to the Google Maps Embed API?

I'm attempting to use the Google Maps Embed API to embed a map matching an address that a user enters. Following the developer guide, I acquired an API Key to add to my API requests. I'm able to successfully pull up a map when I request it via the "src" attribute of an iframe in my React client, like so:
<iframe
...
src={`https://www.google.com/maps/embed/v1/${mode}?key=${apiKey}&q=${encodedAddressQuery}`}
>
But this leaves my API key exposed to the client.
In Google's API Key Best Practices, it's recommended that the API key be stored in an environment variable and that a proxy server be used to safeguard keys. However when I try proxying the request (code below), it seems to return the appropriate map for a split second, but then the iframe replaces the map with an "Oops! Something went wrong" message, and the browser console displays this error:
Google Maps JavaScript API error: UnauthorizedURLForClientIdMapError
https://developers.google.com/maps/documentation/javascript/error-messages#unauthorized-url-for-client-id-map-error
Your site URL to be authorized: http://127.0.0.1:3001/api/maps?parameters=[my_encoded_parameters]
I'm just developing locally at the moment, so I've tried registering http://127.0.0.1:3001/* as an authorized URL as the error documentation suggests, and I've also tried removing all website restrictions on the API key just to see if I was authorizing the wrong URL, but both of those attempts still produced the same error.
I haven't found many resources on setting up a proxy other than this Google Codelabs project, but I haven't been able to pick anything out of it to help with this issue.
Here's the code I'm using in my React front end:
<iframe
...
src={`http://127.0.0.1:3001/api/maps?parameters=${encodedAddressQuery}`}
>
And in my Express Node.js back end:
router.get('/api/maps', async (req: Request, res: Response) => {
try {
const parameters = req.query.parameters;
const map = await MapDataAccessObject.getOne(parameters);
return res.status(OK).send(map.data);
} catch (err) {
...
}
});
export class MapDataAccessObject {
public static async getOne(parameters: string) {
const apiUrl = this.getMapsEmbedUrl(parameters);
const map = await axios.get(apiUrl);
return map;
}
private static getMapsEmbedUrl(parameters: string) {
const encodedParams = encodeURI(parameters);
return `https://www.google.com/maps/embed/v1/place?key=${process.env.MAPS_API_KEY}&q=${encodedParams}`;
};
}
I'm running my React front end and my Node.js back end in Docker containers.
My best guess is that I'm missing a request header that the Maps API is expecting, but I haven't found any documentation on that, or maybe I'm misunderstanding something more fundamental. Any guidance would be very much appreciated.
There is no way to protect your google maps key on a website from being "spied on" because it is always public. The only way to protect your key from foreign usage is to restrict it's access to only the allowed domain/ip-addresses - so if it is used from someone else, it will not work or take anything from your credits (google will show an error message).
https://developers.google.com/maps/documentation/javascript/get-api-key

How do I access a Azure application setting from inside an Aurelia application?

I have a Aurelia app that I host in an Azure app service. I would like to configure the api endpoint that Aurelia connects to by defining it in a Application Setting. How can I read that setting inside Aurelia?
As the other answers and comments also mentioned, Aurelia runs as a clientside application and has no knowledge of backend-driven applications. So the concept of something like the web.config or appsettings.json is not available here without some serious hacks. You don't want to go there.
That being said, of course you can! :) You can pretty much define any settings file you like, similar to the concept of appsettings.json of ASP.NET (Core) apps but then in Aurelia.
A great example for this is the Aurelia open source plugin Aurelia-Configuration.
Simple instructions are that your first start by adding any .json file you like (like config.json) to your project. Next, register it in your Aurelia startup:
export function configure(aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging()
.plugin('aurelia-configuration'); // <-- there you go
aurelia.start().then(a => a.setRoot());
}
Finally, just read out the values using AureliaConfiguration. The sample below illustrates it with dependency injection:
import {inject} from 'aurelia-framework';
import {AureliaConfiguration} from 'aurelia-configuration';
#inject(AureliaConfiguration)
export class MyComponent {
constructor(config) {
this.config = config;
this.config.get('endpoint');
}
}
The README explains it all.
Note: I'm not affiliated with the aurelia-configuration plugin, but just a fan of it.
Isn't Aurelia a JavaScript client framework, e.g. all-in-browser no backend? Application Settings is a server side thing (key-value store) in App Service. No backend, no app settings.
Consider this restify minimal backend that returns Application Settings by calling /settings/{app-setting-name}:
var restify = require('restify');
function respond(req, res, next) {
// Returns app setting value.
// Provides zero input validation,
// DO NOT COPY PASTE INTO PROD,
// ALL YOUR BASE WILL BELONG TO US.
res.send(process.env[req.params.setting]);
next();
}
var server = restify.createServer();
server.get('/settings/:setting', respond);
server.head('/settings/:setting', respond);
server.listen(process.env.PORT || 3000, function() {
console.log('restify listening...');
});
Hope this all makes more sense now.

CORS vs Autofac vs Exception handling middleware in Owin, which one goes first?

I have an owin setup that among many other things, uses CORS and Autofac.
The Autofac documentation says "Register the Autofac middleware FIRST.", many people say app.UseCors should be the very first thing.
I also have an exception handling middleware, which many people also say should be the first thing so that "other middlewares (down the stacktrace) will propagate up and be caught by the try/catch block of this middleware.", and this one makes a lot of sense, since the implementation looks like:
try
{
await Next.Invoke(context);
}
catch...
which one should be first?
what is the correct order for these 3 middleware components
my current Startup Configuration looks like this:
public void Configuration(IAppBuilder app) {
// Add WebApi CORS handling to the OWIN pipeline
app.UseCors(CorsOptions.AllowAll);
// Create and register Owin HttpConfig instance
var config = new HttpConfiguration
{
IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always // Enable error details in http responses
};
// Register the Autofac middleware FIRST. This also adds Autofac-injected middleware
// registered with the container.
var container = AutofacConfig.ConfigureContainer(config);
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
app.UseNLog();
// Handle exceptions from OWIN middleware components globally
app.UseExceptionHandling();
app.UseOAuth(config);
//... quite a bit more stuff after
I think when the documentation says "Register the Autofac middleware FIRST" it is in the context of stating the right order between the following three components: 1) Autofac middleware, 2) Autofac Web API middleware, and 3) the standard Web API middleware.
CORS is not a part of this list, and it should be configured first: you have to call .UseCors before calling .UseWebApi, or any other middleware that uses requests. Any request managed by any middleware should include the CORS Access-Control headers, otherwise you may get cross-origin access errors. I guess this applies also to your error handling middleware. So it should be configured after CORS (but before the other middleware components, if you want to handle their exceptions).
More info about CORS and Web API in this page. More info about CORS details here.
In fact the exact Autofac documentation comment says exactly "Register the Autofac middleware FIRST, then the Autofac Web API middleware, and finally the standard Web API middleware". In the code example in that documentation they have the standard web api setup code, and after that the OWIN web api setup (which is the section where this "First" applies).
public class Startup
{
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
// STANDARD WEB API SETUP:
// Get your HttpConfiguration. In OWIN, you'll create one
// rather than using GlobalConfiguration.
var config = new HttpConfiguration();
// Register your Web API controllers.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// Run other optional steps, like registering filters,
// per-controller-type services, etc., then set the dependency resolver
// to be Autofac.
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
// OWIN WEB API SETUP:
// Register the Autofac middleware FIRST, then the Autofac Web API middleware,
// and finally the standard Web API middleware.
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
app.UseWebApi(config);
}
}
The doc page where I got the code example is here (I guess it is the same you checked).

OWIN static files security

I'm building a SPA that will be sitting in a WebAPI/OWIN app (that will be hosted in IIS) that currently has no MVC components what so ever and the / route will just be index.html.
The whole site will need you to log into Azure AD before you can do anything and we'll then pass a bearer token to the WebAPI calls that are made.
How do you make every request to a static file (or at least, every HTML file) require you to be logged in?
I will tell you how I did it, and how it works for me.
I am using windows authentication, and here is how I configured it:
OwinHttpListener listener = appBuilder.Properties[typeof(OwinHttpListener).FullName] as OwinHttpListener;
listener.Listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;
Then, according to this stackoverflow answer, put the following code between your auth middleware (or the auth code like the above code) and the components you want to protect. It will check to ensure that each request is authenticated.
app.Use(async (context, next) =>
{
var user = context.Authentication.User;
if (user == null || user.Identity == null || !user.Identity.IsAuthenticated)
{
context.Authentication.Challenge();
return;
}
await next();
});
I haven't tested this, but it is what I'd try first so I'm hoping it puts you on the right track.
Configure your app so OWIN is serving all static files via the StaticFilesMiddleware. This article talks about how to do that
Before you register the static file middleware (with the .UseStaticFiles extension method) create and register your own RequireAuthenticationMiddleware that checks if the request is authenticated and if it isn't returns the appropriate response (401, 403 or whatever). You'll want to register this RequireAuthenticationMiddleware after you configure the OWIN Auth middleware so that the auth details are in the OWIN context.
I haven't attempted that using OWIN middleware yet, but you could always fall back to using a HTTP module that checks for the presence of your auth cookie or bearer token?

Resources