Secure your Swagger endpoints using basic authentication
What are we trying to do? We’re trying to lock down our Swagger endpoints (index.html, swagger.json) in order to disallow unauthenticated users from reading our documentation.
Why would I want to do this? Perhaps you’re building a internal API, or an API that should only be available to a few consumers where it’s not important for you to differentiate between them. If this applies to you, then it’s also a nice perk that basic authentication is built into browsers and will trigger a authentication popup, without you needing to supply any UI, or use third-party services.
Well, turns out there isn’t anything built in for doing exactly this, but it doesn’t require too much code to get it in place. In my example I’ll show how to use a single credential taken from a configuration, but it should be easy enough to modify to use other forms of user stores. If you want to do something similar but use OIDC instead of basic authentication (which you’d probably want if you’re having multiple users authenticating against the documentation), then I’d recommend this blog post. I also used this blog when looking up how to do basic authentication in dotnet core, as there isn’t a built in handler for this.
The key points of implementing basic authentication for Swagger are basically:
- Create a basic authentication handler.
- Register a AuthenticationSchema that uses this handler.
- Use
.UseEndpoints()
middleware to intercept the Swagger endpoints, and make sure that they use the AuthenticationSchema above.
Let’s look at the code:
Startup.cs
(I’m assuming that your Startup.cs
looks like this one) and that you’ve setup Swagger using Swashbuckle
ConfigureServices
Key points:
- We’re adding the AuthenticationSchema to our services, making it possible for our middlewares to trigger authentication using it.
- We’re picking out the credentials from our
Configuration
, this is fine if you only want to secure it for minor use/consumers. You should probably modify this to use some form of identity/credential management if you have multiple users accessing this. - If you want to read up on the
Realm
option, then you may have a look here. - We will soon look at the implementation behind
StaticCredentialsBasicAuthenticationhandler
.
Configure
Key points:
- If you’ve already setup Swashbuckle, then the biggest changes here are probably the additions of
.UseAuthentication()
,.UseAuthorization()
, and.UseEndpoints(...)
. "/swagger/{documentName}/swagger.json"
, this mapping relies on you having something similar to thisc.SwaggerEndpoint("/swagger/v1/swagger.json", "My API")
line, if you’re using another pattern, then change the endpoint mapping accordingly.- What we’re doing in
.UseEndpoints(...)
is that we’re mapping the swagger endpoints, that are already mapped in.UseSwagger()
and.UseSwaggerUI(...)
, effectively adding the.RequireAuthorization()
to them, and make sure that it requires authentication using our schema. AuthenticationSchemes = "SwaggerBasic"
uses the same schema name as we used in.AddScheme
inConfigureServices
..UseAuthorization()
must come before.UseSwagger()
and.UseSwaggerUI()
, which must come before.UseEndpoints()
.
Now we’re ready to look at the StaticCredentialsBasicAuthenticationhandler
.
StaticCredentialsBasicAuthenticationhandler.cs
Key points:
HandleChallengeAsync
, this will trigger when anything after it in the pipeline (that uses the same AuthenticationSchema, registered inStartup.cs
) returns a 401 or 403 status code. For instance, ifHandleAuthenticateAsync
fails.HandleAuthenticateAsync
, the flow here is basically:- Check if the client is sending in an
Authorization
header, if not, fail, effectively sending us toHandleChallengeAsync
. - Decode the Base64 string that’s the value of the
Authorization
header. - Validate the username/password that we got from the header. if incorrect, fail it.
- Construct an authentication ticket that will be used by the rest of the pipeline (populating)
ClaimsPrincipal User
on your controllers for instance, this is something that we aren’t using in this case though, as we’re only interested in letting someone reach the Swagger documentation, or not. - Call success using this authentication ticket, thus authenticating the caller.
- Check if the client is sending in an
Resources used when finding this solution
- How to restrict access to swagger/* folder? (the main issue that’s being linked to when trying to find out how to protect a swagger endpoint).
- How to protect swagger endpoint in .NET Core API?
- Securing Swagger with OIDC
- Basic authentication with dotnet core
- Overview of ASP.NET Core authentication
Versions used
- .NET core 3.1
- Swashbuckle.AspNetCore 6.1.4
- Swashbuckle.AspNetCore.Annotations 6.1.4