Developer Notes

Bright pink grafitti on a black brick wall - Photo by Sneep Crew

Dev Proxy for Easy Auth on Container Apps

Like many Azure services, Container Apps support Easy Auth. It's not a very well known feature and that's a bit of a shame because Easy Auth helps to neatly separate the setup and code required for authentication from the implementation of application logic.

Unfortunately, it's not easy (yet) to test apps with Easy Auth enabled in development because it requires HTTP headers from the side car to figure out if a user is logged in.

So, I scratched my own itch, and setup a quick dev proxy using YARP. The code is up on GitHub.

What is Easy Auth?

Easy Auth is, in essence, a sidecar configured to handle authentication on your behalf. It proxies requests to your container app and verifies the authentication headers. It also provides endpoints to login your user to one of the providers you configure.

Outside traffic is directed to a replica of your application through the container apps Ingress (Envoy). The authentication sidecar intercepts the calls, handles token validation and passes the result in headers to the container app.
Outside traffic is directed to a replica of your application through the container apps Ingress (Envoy). The authentication sidecar intercepts the calls, handles token validation and passes the result in headers to the container app.

The sidecar provides information about the logged in user in a set of headers:

HeaderDescription
X-MS-CLIENT-PRINCIPALBase64 encoded JSON object containing all user claims
X-MS-CLIENT-PRINCIPAL-IDPThe name of the identity provider used to login the user. For example 'aad'
X-MS-CLIENT-PRINCIPAL-IDThe user ID. For Azure Entra (AAD) this is the object id for the user.
X-MS-CLIENT-PRINCIPAL-NAMEThe user name. For example the e-mail address used to login.

The JSON object looks like this:

{
    "auth_typ": "aad",
    "claims": [
        {
            "typ": "aud",
            "val": "e44d07cd-7aae-4cd2-aa0e-1bbd9a3865d2"
        },
        // .... many more claims here ...
        {
            "typ": "http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/claims\/emailaddress",
            "val": "marnix@example.org"
        },
        {
            "typ": "name",
            "val": "Marnix van Valen"
        },
        {
            "typ": "preferred_username",
            "val": "marnixn@example.org"
        },
        {
            "typ": "roles",
            "val": "editor"
        },
        {
            "typ": "roles",
            "val": "admin"
        }
    ],
    "name_typ": "http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/claims\/emailaddress",
    "role_typ": "http:\/\/schemas.microsoft.com\/ws\/2008\/06\/identity\/claims\/role"
}

In the example above, I logged in through Azure Entra and all the claims, including roles are passed to container app in a header.

Dev tooling for Easy Auth

Azure App Services and Azure Static Web Apps have similar (but different) implementations. For Azure Static Web Apps there's a handy CLI that launches a dev proxy with a proper local authentication scenario. Microsoft doesn't provide any tooling like this for Azure Container Apps.

Fortunately, creating a dev proxy is easy these days in dotnet, with YARP to handle proxying requests, an API endpoint to handle logout and a of Razor page to fake a user login.

The client app (browser) connects to your back-end service through the development proxy. The Dev Proxy will inject the same headers as the EasyAuth sidecar.
The client app (browser) connects to your back-end service through the development proxy. The Dev Proxy will inject the same headers as the EasyAuth sidecar.

Running the dev proxy

To run the dev proxy, checkout the repo and run the application. You'll need to provide a front-end and back-end url for the proxy:

dotnet run --urls=https://localhost:8888 --backend=http://localhost:5191

It really doesn't matter if you run your back-end in a container or not. You can launch it from visual studio with a debugger attached or from the command line. Just be sure to open the front-end url of the proxy to access your application.

Faking a login

It's easy for the dev proxy to fake a login because it is in control of the headers provided to your application. The Dev Proxy provides hosts a login page at the default url ./auth/login/<idp>, for example: ./auth/login/aad.

The login screen for the EasyAuth Dev Proxy allows you to enter an id, user name and roles.
The login screen for the EasyAuth Dev Proxy allows you to enter an id, user name and roles.

On login the dev proxy sets a cookie to remember your login settings. You can either logout at ./auth/logout or revisit the login page to change the logged in user.

Summary

This solution is nowhere near complete but works well for APIs and server rendered sites. Please check the project on GitHub for more details. It has both the proxy and a demo application.

If you find this useful, please let me know and star the GitHub repo.

No comments yet

Leave a comment

Leave a comment

Please enter your name.
Please enter your e-mail address.
Please enter your comment. You may use basic HTML formatting.

Thank you!

Your comment is submitted and I'll publish it asap.