Reputation: 13
I have bearer token authentication working with OpenIddict 3.0. When a client accesses an authorized controller with a missing token I want it to return error code 401 Unauthorized not 400 Bad Request.
This is where the error message comes from but where does the http status code come from and how would I override it?
OpenIddictValidationHandlers.cs
public ValueTask HandleAsync([NotNull] ProcessAuthenticationContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (string.IsNullOrEmpty(context.Request.AccessToken))
{
context.Logger.LogError("The request was rejected because the access token was missing.");
context.Reject(
error: Errors.InvalidRequest,
description: "The access token is missing.");
return default;
}
context.Token = context.Request.AccessToken;
return default;
}
And my Startup.cs
...
var openId = services.AddOpenIddict()
...
.AddValidation(config =>
{
config.UseLocalServer();
config.UseAspNetCore();
});
Upvotes: 1
Views: 1844
Reputation: 42010
Thanks for reporting this issue, which was fixed in the 3.0.0-alpha1.20163.83
version: OpenIddict will now return a 401 error for requests rejected due to a missing access token.
The event handler responsible of selecting the appropriate HTTP status code is in OpenIddict.Validation.AspNetCore
:
/// <summary>
/// Contains the logic responsible of attaching an appropriate HTTP status code.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by ASP.NET Core.
/// </summary>
public class AttachHttpResponseCode<TContext> : IOpenIddictValidationHandler<TContext> where TContext : BaseRequestContex
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder<TContext>()
.AddFilter<RequireHttpRequest>()
.UseSingletonHandler<AttachHttpResponseCode<TContext>>()
.SetOrder(AttachCacheControlHeader<TContext>.Descriptor.Order - 1_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public ValueTask HandleAsync([NotNull] TContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Response == null)
{
throw new InvalidOperationException("This handler cannot be invoked without a response attached.");
}
// This handler only applies to ASP.NET Core requests. If the HTTP context cannot be resolved,
// this may indicate that the request was incorrectly processed by another server stack.
var response = context.Transaction.GetHttpRequest()?.HttpContext.Response;
if (response == null)
{
throw new InvalidOperationException("The ASP.NET Core HTTP request cannot be resolved.");
}
response.StatusCode = context.Response.Error switch
{
null => 200,
Errors.InvalidToken => 401,
Errors.MissingToken => 401,
Errors.InsufficientAccess => 403,
Errors.InsufficientScope => 403,
_ => 400
};
return default;
}
}
Upvotes: 1