· 4 min read

How to Bypass Umbraco Cloud Basic Auth for Preview URLs

Learn how to bypass Umbraco Cloud's Basic Authentication for specific preview URLs using the built-in shared secret feature and a lightweight ASP.NET middleware, without exposing the rest of your staging site.

Some background context for you

My friend and fellow Umbraco MVP Richard Ockerby has created a really cool package called Umbraco Rollback Previewer. The main function of the package is to allow editors to see a visual difference when using the rollback features of the Umbraco backoffice.

Normally you just see the difference in the JSON values but that isn't very friendly. So Richard created the package to allow you to see side by side previews of the two versions you are comparing. One of the great thing about this is that it is able to render a preview for an unpublished content item, which gave me an idea.

// Check it out

Rollback Previewer Package

This is such a great package, you should definitely try it out if you haven't done already.

That got me thinking

If you can generate a preview for previous versions, even unpublished ones, then that opens it up to another use. What about sharing a preview url for an unpublished change with people who don't have access to Umbraco? I had a client who needed this feature, and I was intrigued to see if it could work. So I messaged Richard.

Screenshot of my conversation with Richard about this feature

As you can see, Richard responded very quickly about it, and we ended up having a pair programming session that week and in just a matter of days Richard had fully developed the feature and added it to his amazing package.

What has this got to do with bypassing basic auth?

That is just the backstory to this post, our client needed to be able to share a preview URL for content which was on their staging site, which made things even more tricky because staging sites on Umbraco Cloud are restricted behind basic authentication. I needed to find a way around it.

So I reached out to the Umbraco Community for some more help.

Me asking for help with basic auth in the Umbraco Slack and Jon Witter and Jesper Mayntzhusen coming to the rescue

I Found a Solution

So with the help of the Umbraco Community I learned that Umbraco Cloud's Basic Authentication has a Shared Secret feature built in, which lets you bypass the authentication by sending a specific HTTP header with the right value. So the solution was to write a small middleware that intercepts requests to certain URLs and automatically adds that header before Umbraco’s auth middleware ever sees the request.

The Config

First, add a new section to your appsettings.json for the paths you want to allow through.

NOTE: /ucrbp is the path which the Umbraco Community Rollback Previewer package uses for sharing previews.

"BasicAuthBypass": {
  "AllowedPaths": [
    "/ucrbp",
    "/another-preview-page"
  ]
}

Make sure you also have Basic Auth enabled and your shared secret configured in the Umbraco section:

"Umbraco": {
  "CMS": {
    "BasicAuth": {
      "Enabled": true,
      "SharedSecret": {
        "HeaderName": "X-Authentication-Shared-Secret",
        "Value": "your-secret-value-here"
      }
    }
  }
}

The Settings Class

Create a simple settings class to hold the allowed paths:

public class BasicAuthBypassSettings
{
    public List<string> AllowedPaths { get; set; } = [];
}

The Middleware

The middleware checks whether the incoming request path matches any of your allowed paths. If it does, it reads the shared secret header name and value from the Umbraco config and adds it to the request headers. Umbraco then sees the header and lets the request through.

public class BasicAuthBypassMiddleware
{
    private readonly RequestDelegate _next;
    private readonly BasicAuthBypassSettings _settings;
    private readonly string _headerName;
    private readonly string? _secretValue;

    public BasicAuthBypassMiddleware(RequestDelegate next, IOptions<BasicAuthBypassSettings> settings, IConfiguration configuration)
    {
        _next = next;
        _settings = settings.Value;
        _headerName = configuration
                          .GetSection("Umbraco:CMS:BasicAuth:SharedSecret")
                          .GetValue<string>("HeaderName")
                      ?? "X-Authentication-Shared-Secret";
        _secretValue = configuration
            .GetSection("Umbraco:CMS:BasicAuth:SharedSecret")
            .GetValue<string>("Value");
    }

    public async Task InvokeAsync(HttpContext context)
    {
        if (_settings.AllowedPaths.Any(p => context.Request.Path.StartsWithSegments(p)))
        {
            context.Request.Headers[_headerName] = _secretValue;
        }

        await _next(context);
    }
}

Registering in Program.cs

We only want to register this middleware if Basic Auth is actually enabled, so we wrap the registration in a check:

builder.Services.Configure<BasicAuthBypassSettings>(
    builder.Configuration.GetSection("BasicAuthBypass"));

// ...

if (builder.Configuration.GetValue<bool>("Umbraco:CMS:BasicAuth:Enabled"))
{
    app.UseMiddleware<BasicAuthBypassMiddleware>();
}

app.UseUmbraco()
// ...

Make sure the middleware is registered before UseUmbraco() so it runs first in the pipeline.

That's it

Now any request to a path in your AllowedPaths list will automatically have the shared secret header added and will bypass Basic Authentication. Everything else continues to be protected as normal.

Let me know what you think about this in the comments below.

Comments and reactions