Article guideContents, topics, tags, and RSS
What Basic authentication provides
Basic authentication is a simple HTTP authentication scheme. A client sends a user name and password with each request in an Authorization header. It can still be useful in tightly controlled integrations, but it is usually a poor default for an interactive web application.
The credentials are Base64-encoded, not encrypted. Anyone who can observe an unprotected request can recover them, so HTTPS is mandatory. Browsers may also cache credentials and send them automatically, which makes logout behavior and cross-site request forgery protection less straightforward.
Advantages
- Defined by the current RFC 7617 standard.
- Widely supported by HTTP clients.
- Simple for controlled service-to-service scenarios.
Limitations
- Credentials are sent with every authenticated request.
- Base64 offers no confidentiality.
- Browser logout behavior is limited.
- Automatically attached credentials can create CSRF risk.
- The scheme does not provide modern token lifetimes, scopes, or revocation.
Historical implementations
The following filter and middleware examples are retained to explain the original 2018 implementation. They should not be copied into a new application without adding robust parsing, credential storage, constant-time comparison where appropriate, logging controls, and tests. Current ASP.NET Core applications should integrate a scheme through the authentication system.
Attribute and filter solution
The filter approach made it possible to opt in at controller or action level.
Example code
BasicAuthorizeAttribute.cs
// -------------------------------------------------------------------------------------------------
// Copyright (c) Johan Boström. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
// -------------------------------------------------------------------------------------------------
namespace API.BasicAuth.Attributes
{
using System;
using Microsoft.AspNetCore.Mvc;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class BasicAuthorizeAttribute : TypeFilterAttribute
{
public BasicAuthorizeAttribute(string realm = null)
: base(typeof(BasicAuthorizeFilter))
{
Arguments = new object[]
{
realm
};
}
}
}
BasicAuthorizeFilter.cs
// -------------------------------------------------------------------------------------------------
// Copyright (c) Johan Boström. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
// -------------------------------------------------------------------------------------------------
namespace API.BasicAuth.Attributes
{
using System;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
public class BasicAuthorizeFilter : IAuthorizationFilter
{
private readonly string realm;
public BasicAuthorizeFilter(string realm = null)
{
this.realm = realm;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
string authHeader = context.HttpContext.Request.Headers["Authorization"];
if (authHeader != null && authHeader.StartsWith("Basic "))
{
// Get the encoded username and password
var encodedUsernamePassword = authHeader.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries)[1]?.Trim();
// Decode from Base64 to string
var decodedUsernamePassword = Encoding.UTF8.GetString(Convert.FromBase64String(encodedUsernamePassword));
// Split username and password
var username = decodedUsernamePassword.Split(':', 2)[0];
var password = decodedUsernamePassword.Split(':', 2)[1];
// Check if login is correct
if (IsAuthorized(username, password))
{
return;
}
}
// Return authentication type (causes browser to show login dialog)
context.HttpContext.Response.Headers["WWW-Authenticate"] = "Basic";
// Add realm if it is not null
if (!string.IsNullOrWhiteSpace(realm))
{
context.HttpContext.Response.Headers["WWW-Authenticate"] += $" realm=\"{realm}\"";
}
// Return unauthorized
context.Result = new UnauthorizedResult();
}
// Make your own implementation of this
public bool IsAuthorized(string username, string password)
{
// Check that username and password are correct
return username.Equals("User1", StringComparison.InvariantCultureIgnoreCase)
&& password.Equals("SecretPassword!");
}
}
}
Example usage
SecuredController.cs
// -------------------------------------------------------------------------------------------------
// Copyright (c) Johan Boström. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
// -------------------------------------------------------------------------------------------------
namespace API.BasicAuth.Controllers
{
using System.Collections.Generic;
using Attributes;
using Microsoft.AspNetCore.Mvc;
[BasicAuthorize("my-example-realm.com")]
[Route("api/[controller]")]
public class SecuredController : Controller
{
// GET api/secured
[HttpGet]
public IEnumerable<string> Get()
{
return new[] { "Secret 1", "Secret 2" };
}
}
}
Middleware solution
The alternative was a custom ASP.NET Core middleware. This sample secures the entire site, although middleware can be mapped to selected branches.
Example code
After creating the middleware, the original application registered it with app.UseMiddleware<BasicAuthMiddleware>("example-realm.com"); in Startup.cs.
BasicAuthMiddleware.cs
// -------------------------------------------------------------------------------------------------
// Copyright (c) Johan Boström. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
// -------------------------------------------------------------------------------------------------
namespace API.BasicAuth.Middlewares
{
using System;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
public class BasicAuthMiddleware
{
private readonly RequestDelegate next;
private readonly string realm;
public BasicAuthMiddleware(RequestDelegate next, string realm)
{
this.next = next;
this.realm = realm;
}
public async Task Invoke(HttpContext context)
{
string authHeader = context.Request.Headers["Authorization"];
if (authHeader != null && authHeader.StartsWith("Basic "))
{
// Get the encoded username and password
var encodedUsernamePassword = authHeader.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries)[1]?.Trim();
// Decode from Base64 to string
var decodedUsernamePassword = Encoding.UTF8.GetString(Convert.FromBase64String(encodedUsernamePassword));
// Split username and password
var username = decodedUsernamePassword.Split(':', 2)[0];
var password = decodedUsernamePassword.Split(':', 2)[1];
// Check if login is correct
if (IsAuthorized(username, password))
{
await next.Invoke(context);
return;
}
}
// Return authentication type (causes browser to show login dialog)
context.Response.Headers["WWW-Authenticate"] = "Basic";
// Add realm if it is not null
if (!string.IsNullOrWhiteSpace(realm))
{
context.Response.Headers["WWW-Authenticate"] += $" realm=\"{realm}\"";
}
// Return unauthorized
context.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
}
// Make your own implementation of this
public bool IsAuthorized(string username, string password)
{
// Check that username and password are correct
return username.Equals("User1", StringComparison.InvariantCultureIgnoreCase)
&& password.Equals("SecretPassword!");
}
}
}
What to use now
For new code, use ASP.NET Core’s authentication abstractions so the scheme participates consistently in challenges, authorization, dependency injection, logging, and testing. Derive a scheme handler from AuthenticationHandler<TOptions> if Basic authentication is genuinely required, or choose a standard token, cookie, or federated flow that fits the client.
The original examples remain in zarxor/Example.API.Secured for historical reference.
Whatever implementation you choose, do not send Basic credentials over plain HTTP.
