Developing Azure Functions could be cumbersome if you want to use App Service Authentication feature. While it works flawlessly when a function is deployed, it brings many unfair challenges when working locally(mostly because of a need to create an artificial mock of identity provider and injecting it somehow). I decided to give it a try and modify Azure Functions CLI a little bit, to it has that feature implemented already. Surprisingly it was easier that I thought.
How does Azure Functions CLI work?
When you're working with Functions locally, when you hit F5, you'll see a local runtime starting and ready to handle a request(or trigger a function). In fact VS starts it using Azure Functions CLI and invoking following command:
> func start
Local instance of runtime with a boilerplate function enabled
When this local host is started, it handles multiple features like:
- loading settings from local.settings.json
- starting a runtime
- providing an endpoint to handle HTTP requests
- ...and many more
In general you're able to pass many different parameters so you can start a host listening on a specific port, with HTTPS enabled and CORS configured. You can do it like so:
func start --useHttps=true --cors=*
I got an idea to extend parameters so you can do following:
func start --security=true
So each function invocation has to be challenged against a custom security provider. How did I achieve this?
Handling a parameter
The very first thing I had to do was to handle a security parameter in CLI. To do so I modified StartHostAction class which parses inline arguments when CLI is started. Added line looks like this:
.WithDescription("Enable securing HTTP functions using available providers")
.Callback(s => Security = s);
This was super easy. Let's do something more difficult - use this parameter so some logic is performed.
Securing each request
Because CLI is built against new ASP.NET Core pipeline, you have to provide a custom middleware so each request has to pass through it. There's a Startup class, which is the foundation of the whole host. There you can inject your functionality as I did:
app.Use(async (context, next) =>
var provider = new SecurityProvider();
var authenticationResult = provider.IsAuthenticated(context.Request);
if (authenticationResult == false)
context.Response.StatusCode = 401;
Now if security is enabled, each request will be validated using some SecurityProvider,which is a custom class implementing following interface:
internal interface ISecurityProvider
bool IsAuthenticated(HttpRequest req);
How does it work?
Now when I start CLI using following command:
func start --security=true
I'm getting the following result:
When I disable security:
Of course the error in the second response comes from the boilerplate function because I didn't pass name parameter.
Now since IsAuthenticated() has a full request passed, you can implement whichever flow you want, starting from a very basic one like me:
public class SecurityProvider : ISecurityProvider
public bool IsAuthenticated(HttpRequest req)
In the next episode I'll try to enhance this solution a little bit, so ISecurityProvider will be loaded from a Function App you'll be developing locally(so it gains much flexibility). For now you can following this issue on GitHub, where I proposed solution I described above.