I've been trying to look into how I can disable SSL certificate validation by the HttpClient used to send request to the authorization metadata endpoint. I am running the authoriation server locally using hostname idp.local.test.com with haproxy as a reverse proxy using self signed certificate.
When I test locally the client token validation (no introspection) the HttpClient used to send GET request to metadata endpoint throws SSL validation error because I'm using self-signed certificate.
Here is the output of the log:
info: System.Net.Http.HttpClient.OpenIddict.Validation.SystemNetHttp.ClientHandler[100]
Sending HTTP request GET https://idp.local.test.com/.well-known/openid-configuration
System.Net.Http.HttpClient.OpenIddict.Validation.SystemNetHttp.ClientHandler: Information: Sending HTTP request GET https://idp.local.test.com/.well-known/openid-configuration
Loaded '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/3.1.5/System.Diagnostics.StackTrace.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
Loaded '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/3.1.5/System.Reflection.Metadata.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
dbug: OpenIddict.Validation.OpenIddictValidationDispatcher[0]
An exception was thrown by OpenIddict.Validation.SystemNetHttp.OpenIddictValidationSystemNetHttpHandlers+SendHttpRequest`1[[OpenIddict.Validation.OpenIddictValidationEvents+ApplyConfigurationRequestContext, OpenIddict.Validation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=35a561290d20de2f]] while handling the OpenIddict.Validation.OpenIddictValidationEvents+ApplyConfigurationRequestContext event.
System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
at System.Net.Security.SslStream.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, ExceptionDispatchInfo exception)
at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.PartialFrameCallback(AsyncProtocolRequest asyncRequest)
--- End of stack trace from previous location where exception was thrown ---
at System.Net.Security.SslStream.ThrowIfExceptional()
at System.Net.Security.SslStream.InternalEndProcessAuthentication(LazyAsyncResult lazyResult)
at System.Net.Security.SslStream.EndProcessAuthentication(IAsyncResult result)
at System.Net.Security.SslStream.EndAuthenticateAsClient(IAsyncResult asyncResult)
at System.Net.Security.SslStream.<>c.<AuthenticateAsClientAsync>b__65_1(IAsyncResult iar)
at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
This is the Startup class in the client application:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(options =>
{
options.DefaultScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
});
services.AddOpenIddict()
.AddValidation(options =>
{
// options.Configure(config =>
// {
// config.MetadataAddress = new Uri("/.well-known/openid-configuration");
// });
var section = Configuration.GetSection("OAuth");
var tokenEncryptionKey = section.GetValue<string>("TokenEncryptionKey");
var issuer = section.GetValue<string>("Issuer");
options.SetIssuer(issuer);
options.AddEncryptionKey(new SymmetricSecurityKey(
Convert.FromBase64String(tokenEncryptionKey)
));
options.UseSystemNetHttp();
options.UseAspNetCore();
});
services.AddControllers(options =>
{
options.Filters.Add(typeof(GlobalExceptionFilter));
var requireAuthPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(requireAuthPolicy));
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
And this is the authorization server code:
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddPasswordlessLoginTokenProvider()
.AddEmailConfirmationTokenProvider()
.AddPasswordResetTokenProvider();
services.AddOpenIddict()
.AddCore(coreBuilder =>
{
coreBuilder.SetDefaultApplicationEntity<OIDCApplication>()
.UseEntityFrameworkCore()
.UseDbContext<ApplicationDbContext>();
})
.AddServer(serverBuilder =>
{
serverBuilder.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles, Scopes.OfflineAccess);
serverBuilder.SetAuthorizationEndpointUris("/connect/authorize")
.SetTokenEndpointUris("/connect/token")
.SetConfigurationEndpointUris("/.well-known/openid-configuration")
.SetUserinfoEndpointUris("/connect/userinfo")
.SetIntrospectionEndpointUris("/connect/introspect");
serverBuilder.SetAuthorizationCodeLifetime(TimeSpan.FromMinutes(5));
string issuerHostname = Configuration["IssuerHost"];
serverBuilder.SetIssuer(new Uri($"https://{issuerHostname}"));
serverBuilder.Configure(options =>
{
options.UseSlidingExpiration = true;
});
serverBuilder.AllowAuthorizationCodeFlow()
.AllowRefreshTokenFlow();
serverBuilder.UseAspNetCore()
.EnableAuthorizationEndpointPassthrough()
.EnableTokenEndpointPassthrough()
.EnableUserinfoEndpointPassthrough()
.DisableTransportSecurityRequirement(); // Remove on prod
var tokenEncryptionKey = Configuration.GetValue<string>("TokenEncryptionKey");
serverBuilder.AddEncryptionKey(new SymmetricSecurityKey(
Convert.FromBase64String(tokenEncryptionKey)
));
serverBuilder.AddDevelopmentSigningCertificate();
})
.AddValidation(options =>
{
options.UseLocalServer();
options.UseAspNetCore();
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHsts();
app.UseMiddleware<Middlewares.LoggingMiddleware>();
app.UseExceptionHandler("/Home/Error");
app.UseStatusCodePagesWithReExecute("/Home/Status", "?code={0}");
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
Cheers