CoreWCF / CoreWCF/CoreWCF

Basic Authentication - Request.Headers["Authorization"] is always null

Open
#832 7 comments 1 reaction 0 assignees View on GitHub
Dominant language
C#
Stars
1.8k
Forks
320
PR merge metrics
No merged PRs in 30d

Description

### Discussed in https://github.com/CoreWCF/CoreWCF/discussions/827

Originally posted by **enricofacchinetti** September 9, 2022
Hi,
here is our code implementing basic authentication in CoreWCF.
We are aware of the protocol's security issues and in the next future we will replace it; but in the short term we have to meet some backward compatibility requirements.
In the Program.cs file, Just before to invoke the method app.UseServiceModel() we implemented a middleware to launch the basic authentication.

The method HandleAuthenticateAsync() is then invoked in a custom class inheriting AuthenticationHandler.
But in this method, when it's up to fetch the Authorization header invoking Request.Headers["Authorization"], a null object is returned.
We would like to point out that the other headers are present in the IHeaderDictionary Request.Headers.

We tried to set the BasicHttpSecurityMode to Transport and to TransportWithMessageCredential.
But in both the cases, the result doesn't change.
The client we use to call our CoreWCF service during our tests is SoapUI 5.7.0.
We set the Authorization section of SoapUI 5.7.0. to Basic and we set the credentials.
We tried to find something relevant in the documentation and in the forum but without success.
Does anybody has any ideas on how to fix this problem?
Thanks in advance for your precious help.
E.

// Program.cs

`
var builder = WebApplication.CreateBuilder();

builder.Configuration.AddJsonFile("libsettings.json", optional: true, reloadOnChange: true);
builder.Configuration.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
builder.Configuration.AddEnvironmentVariables();

builder.WebHost.ConfigureKestrel((context, options) =>
{
options.AllowSynchronousIO = true;
});

// Add WSDL support
builder.Services.AddServiceModelServices();
builder.Services.AddServiceModelMetadata();
builder.Services.AddSingleton();

builder.Services.AddTransient((provider) =>
{
var libSettings = builder.Configuration.GetSection("LibSettings").Get();
var connString = builder.Configuration.GetConnectionString("MyConnString");
var contextOptions = new DbContextOptionsBuilder()
.UseSqlServer(connString)
.Options;
return new MySoapServiceClass(libSettings, contextOptions);
});

builder.Services.AddControllers();

if (ServicePointManager.SecurityProtocol.HasFlag(SecurityProtocolType.Tls12) == false)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
}

builder.Services.AddAuthentication("BasicAuthentication").
AddScheme
("BasicAuthentication", null);

var app = builder.Build();

app.Use(async (context, next) =>
{
// Only check for basic auth when path is for the TransportWithMessageCredential endpoint only
if (context.Request.Path.StartsWithSegments("/myBaseAddress/myservice"))
{
// Check if currently authenticated
var authResult = await context.AuthenticateAsync("BasicAuthentication");
if (authResult.None)
{
// If the client hasn't authenticated, send a challenge to the client and complete request
await context.ChallengeAsync("BasicAuthentication");
return;
}
}
// Call the next delegate/middleware in the pipeline.
await next(context);
});

app.UseServiceModel(builder =>
{
builder.AddService(serviceOptions =>
{
serviceOptions.DebugBehavior.IncludeExceptionDetailInFaults = true;
serviceOptions.BaseAddresses.Add(new Uri("http://localhost/myBaseAddress"));
serviceOptions.BaseAddresses.Add(new Uri("https://localhost/myBaseAddress"));
serviceOptions.DebugBehavior.HttpsHelpPageEnabled = true;
serviceOptions.DebugBehavior.HttpsHelpPageUrl = new Uri("https://localhost/myBaseAddress/help");
serviceOptions.DebugBehavior.HttpHelpPageUrl = new Uri("http://localhost/myBaseAddress/help");
})
.AddServiceEndpoint(new BasicHttpBinding(BasicHttpSecurityMode.Transport), "/myservice");
var serviceMetadataBehavior = app.Services.GetRequiredService();
serviceMetadataBehavior.HttpsGetEnabled = true;
});

app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});

app.Run();

public class BasicAuthenticationHandler : AuthenticationHandler
{
public BasicAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger,
UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
}

protected override Task HandleAuthenticateAsync()
{
try
{
var auth = (string)Request.Headers["Authorization"];

// Authorization header is always null but the client sends it
if (string.IsNullOrEmpty(auth)) return Task.FromResult(AuthenticateResult.Fail("Invalid Credentials"));
}
catch
{
return Task.FromResult(AuthenticateResult.Fail("Error Occured.Authorization failed."));
}
}
}
`

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.