Added in Unreleased.
ZeeKayDa.Auth includes a SecretSanitizingLogger wrapper that intercepts every log call made by the library’s own services and redacts known-sensitive OAuth parameters — client_secret, code_verifier, Authorization, access_token, refresh_token, and others — before they reach the underlying log sink. In addition, all logged exceptions are unconditionally wrapped in RedactedExceptionWrapper, replacing the exception Message with the fixed placeholder [exception message redacted by SecretSanitizingLogger] before the exception reaches any log sink.
⚠️ Warning: The library’s redaction boundary covers only ZeeKayDa.Auth’s own internal logging. ASP.NET Core’s
UseHttpLogging(), Kestrel’s connection logging, W3CLogger, Application Insights request telemetry, and custom exception-handling middleware all emit log entries entirely outside this wrapper. Any sensitive value those components capture — including theAuthorizationheader or aclient_secretposted in a form body — will reach your log sinks verbatim unless you take the steps described in this guide.
What ZeeKayDa.Auth does and does not cover
| Logging surface | Covered by SecretSanitizingLogger? |
|---|---|
| ZeeKayDa.Auth internal structured logs | Yes — values for sensitive keys are replaced with [REDACTED] |
| Exception messages on logged exceptions | Yes — covered unconditionally; all logged exceptions are wrapped in RedactedExceptionWrapper and their Message is replaced with a fixed placeholder |
ASP.NET Core UseHttpLogging() | No |
| Kestrel connection/request logging | No |
| W3CLogger | No |
| Application Insights request telemetry | No |
| Exception-handling middleware | No |
Your own application code calling ILogger<T> directly | No |
1. Do not enable request-body logging on OAuth endpoints
UseHttpLogging() can be configured to log the request body. Because token endpoint requests (/connect/token) send credentials as application/x-www-form-urlencoded form fields — including client_secret and code_verifier — enabling body logging on those routes will write plaintext credentials to your logs.
If you need UseHttpLogging() for other routes, limit it explicitly:
// Register the middleware but apply it per-route, not globally.
app.UseHttpLogging();
// Then, on the routes where you want logging:
app.MapGet("/api/some-safe-endpoint", handler)
.WithHttpLogging(HttpLoggingFields.All);
// Do NOT apply WithHttpLogging to ZeeKayDa.Auth endpoints.
// app.MapZeeKayDaAuth() registers routes under the issuer path;
// leave those routes without WithHttpLogging.
app.MapZeeKayDaAuth();
If you call app.UseHttpLogging() globally without per-route scoping, disable body logging in the HttpLoggingOptions:
builder.Services.AddHttpLogging(logging =>
{
// Omit RequestBody and ResponseBody from the logged fields.
logging.LoggingFields = HttpLoggingFields.RequestHeaders
| HttpLoggingFields.ResponseHeaders
| HttpLoggingFields.ResponseStatusCode;
});
app.UseHttpLogging();
app.MapZeeKayDaAuth();
💡 Tip:
HttpLoggingFields.RequestBodyandHttpLoggingFields.ResponseBodyare the fields that write credential material. Removing them still lets you log headers, status codes, and durations, which are useful for diagnostics without being security-sensitive.
2. Redact the Authorization header at the host level
The token endpoint accepts client_secret_basic credentials as an HTTP Authorization: Basic … header. UseHttpLogging() logs request headers by default. Redact the Authorization header before it is written to the log:
builder.Services.AddHttpLogging(logging =>
{
// Log request headers, but redact Authorization.
logging.LoggingFields = HttpLoggingFields.RequestHeaders
| HttpLoggingFields.ResponseStatusCode;
// Headers not in RequestHeaders are suppressed; adding Authorization here
// explicitly marks it for redaction (shown as "[Redacted]" in the log output).
logging.RequestHeaders.Remove("Authorization");
});
HttpLoggingOptions.RequestHeaders is an allowlist: only headers explicitly listed are logged as their actual value. Headers absent from the list are logged as [Redacted]. Removing Authorization from the set (or simply not adding it) ensures the header value is never written to the log.
⚠️ Warning: The
Authorizationheader is not in the defaultRequestHeadersallowlist in ASP.NET Core 8+, so the default configuration already redacts it. Confirm this remains true for the version of ASP.NET Core you are targeting and do not add"Authorization"back to the allowlist.
3. Suppress Kestrel’s detailed request logging in production
Kestrel emits connection-level and request-level debug logs under the Microsoft.AspNetCore.Server.Kestrel category. These logs do not typically include header or body values, but they do include raw URLs which may contain sensitive query parameters on authorization endpoints.
Suppress below Warning in production:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore.Server.Kestrel": "Warning"
}
}
}
4. Guard exception-handling middleware
ASP.NET Core’s developer exception page and any custom exception middleware may log or render exception details — including the HttpContext, which carries the full request including headers and form fields. Do not enable the developer exception page in production, and ensure your production exception handler does not log HttpContext or HttpRequest objects:
if (app.Environment.IsDevelopment())
{
// Developer exception page is safe in development only.
app.UseDeveloperExceptionPage();
}
else
{
// In production, use a minimal error handler that does not log request details.
app.UseExceptionHandler("/error");
}
If you write a custom exception handler that logs exceptions, log only the exception itself — not context.Request.Form, context.Request.Headers, or any property that could contain credential values.
5. Check Application Insights and other telemetry SDKs
Application Insights, OpenTelemetry collectors, and similar SDKs may capture the full HttpRequest as part of a dependency or request telemetry item. Consult the documentation for your telemetry SDK and configure it to:
- Exclude
Authorizationandclient_secretfrom captured request properties. - Disable request-body capture on OAuth endpoints.
For Application Insights specifically, use a TelemetryInitializer to scrub sensitive properties before they are sent:
public sealed class OAuthTelemetrySanitizer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
if (telemetry is RequestTelemetry request)
{
// Remove the Authorization header from captured telemetry.
request.Properties.Remove("Authorization");
}
}
}
Register the initializer in DI:
builder.Services.AddSingleton<ITelemetryInitializer, OAuthTelemetrySanitizer>();
💡 Tip: The pattern shown above is a starting point. Your specific telemetry SDK and data model will determine exactly which properties to scrub. Review the captured telemetry in a staging environment before deploying to production.
Summary
| Action | Prevents |
|---|---|
Avoid HttpLoggingFields.RequestBody on OAuth routes | client_secret, code_verifier, and other form fields in logs |
Remove Authorization from HttpLoggingOptions.RequestHeaders | Token endpoint Basic credentials in HTTP logs |
Set Kestrel log level to Warning in production | Verbose connection noise; URL-embedded parameters |
| Use a non-verbose production exception handler | Full request state (headers, form) in exception logs |
| Sanitize telemetry SDK captured properties | Credentials appearing in APM or tracing back-ends |
Opting out of exception message sanitization in development
Exception message sanitization is active by default for all environments. If you need to see original exception messages in your log output during local development, set Logging.DisableExceptionSanitizing to true in appsettings.Development.json:
// appsettings.Development.json
{
"ZeeKayDaAuth": {
"Logging": {
"DisableExceptionSanitizing": true
}
}
}
Because this setting is in appsettings.Development.json, it is automatically scoped to the Development environment. The standard ASP.NET Core configuration layering means this file is loaded only when ASPNETCORE_ENVIRONMENT or DOTNET_ENVIRONMENT is set to Development — it is never applied in production or staging environments.
When DisableExceptionSanitizing is true, ZeeKayDa.Auth emits a LogLevel.Warning at startup as a reminder that exception messages will not be redacted.
⚠️ Warning:
DisableExceptionSanitizingis for development environments only. Never set it totrueinappsettings.jsonorappsettings.Production.json. Exception messages can contain credential values or other sensitive state that would reach your log sinks verbatim. Always set this flag only inappsettings.Development.json.
Compile-time enforcement inside ZeeKayDa.Auth
In addition to the runtime steps above, ZeeKayDa.Auth ships Roslyn analyzer rules that enforce log-hygiene requirements at build time for code inside the ZeeKayDa.* namespace. ZEEKAYDA0001 prevents ILogger<T> from being injected directly (bypassing SecretSanitizingLogger), and ZEEKAYDA0002 prevents interpolated strings containing sensitive identifiers from being passed to Log* methods (which would embed credential values before the redaction layer can act). See the Analyzer rules reference for the full rule definitions, violation examples, and suppression guidance.
See also
- Analyzer rules reference — ZEEKAYDA0001 and ZEEKAYDA0002 compile-time log-hygiene rules
- Implement a custom extension point — security contracts for custom hashers and authenticators
- Configure ZeeKayDa.Auth — register the framework and the minimum required options
- HttpLogging in ASP.NET Core — Microsoft’s reference for
UseHttpLoggingandHttpLoggingFields