0

I was trying to return the custom error response message, when Unauthorized request is made to api. I have tried several event handler to alter the responses, but none of them seems to work in my case.

What is the proper openiddict event handler to alter the responses when there is unauthorized request?

What i have tried so far.

public class CustomAuthorizationHandler : IOpenIddictServerEventHandler<OpenIddictServerEvents.ApplyTokenResponse>
{

    public Task HandleAsync(OpenIddictServerEvents.ApplyTokenResponse notification, CancellationToken cancellationToken)
    {

    }


}
public class CustomAuthorizationResponseHandler : IOpenIddictServerEventHandler<OpenIddictServerEvents.ApplyAuthorizationResponse>
{

    public Task HandleAsync(OpenIddictServerEvents.ApplyAuthorizationResponse notification, CancellationToken cancellationToken)
    {

    }

}
public class CustomValidateAuthorizationRequestHandler : IOpenIddictServerEventHandler<OpenIddictServerEvents.HandleAuthorizationRequest>
{

    public Task HandleAsync(OpenIddictServerEvents.HandleAuthorizationRequest notification, CancellationToken cancellationToken)
    {

    }

}

Add Server in Startup.cs

        services.AddOpenIddict().AddCore(options =>
            {
                options.UseEntityFrameworkCore()
                       .UseDbContext<AWSContext>()
                       .ReplaceDefaultEntities<Guid>();

            }).AddServer(options =>
            {
                options.UseMvc();
                options.EnableAuthorizationEndpoint("/connect/authorize")
                         .EnableTokenEndpoint("/connect/token")
                         .EnableLogoutEndpoint("/connect/logout")
                         .EnableIntrospectionEndpoint("/connect/introspect")
                         .EnableUserinfoEndpoint("/api/userinfo");
                options.AllowClientCredentialsFlow();

                options.RegisterScopes(OpenIdConnectConstants.Scopes.Email,
                                       OpenIdConnectConstants.Scopes.Profile,

OpenIddictConstants.Scopes.Roles);

                options.AddEphemeralSigningKey();

                options.AllowImplicitFlow();
                options.DisableHttpsRequirement();
                options.AddEventHandler<OpenIddictServerEvents.ApplyTokenResponse, CustomAuthorizationHandler>();
                options.AddEventHandler<OpenIddictServerEvents.ApplyAuthorizationResponse, CustomAuthorizationResponseHandler>();
                options.AddEventHandler<OpenIddictServerEvents.HandleAuthorizationRequest, CustomValidateAuthorizationRequestHandler>();
                //options.AddDevelopmentSigningCertificate();
                options.UseJsonWebTokens();
            });//.AddValidation();

Controller

[HttpGet("~/home/message")]
[Authorize(AuthenticationSchemes = OpenIddictValidationDefaults.AuthenticationScheme)]
public async Task<IActionResult> GetMessage()
{
    var subject = User.FindFirst(OpenIdConnectConstants.Claims.Subject)?.Value;
    if (string.IsNullOrEmpty(subject))
    {
        return BadRequest();
    }

    var application = await _applicationManager.FindByClientIdAsync(subject, HttpContext.RequestAborted);
    if (application == null)
    {
        return BadRequest();
    }

    return Content($"{application.DisplayName} has been successfully authenticated.");
}

Postman getting 401 error:

enter image description here

Nan Yu
  • 26,101
  • 9
  • 68
  • 148
User_4373
  • 379
  • 1
  • 3
  • 17
  • You may check the OpenIddict validation handler :https://github.com/openiddict/openiddict-core/issues/594 . If the token is jwt , you can also use `AddJwtBearer` middleware to customize the response : https://stackoverflow.com/a/48736096/5751404 – Nan Yu Dec 26 '19 at 06:20
  • I have been already trying to use the `OpenIddict` validation handler as mention in the github-issue, but i am not aware what handler is the correct one in my case! as trying like in the stackoverflow link you posted shows no error when invoking the api with without the `acess-token` – User_4373 Dec 26 '19 at 06:52
  • you should apply authorzie attribute with correct policy name on protected controller/action , also don't forget to add `app.UseAuthentication();` in Configure .Add a breakpoint in jwt event to check whether the event fires . – Nan Yu Dec 26 '19 at 06:59
  • For your first question use `IOpenIddictValidationEventHandler` if you want to check whether token exists – Nan Yu Dec 26 '19 at 07:04
  • Or try `ApplyChallenge` event to custmize . – Nan Yu Dec 26 '19 at 07:06
  • yeah it hit the `jwt event` in `breakpoint`, but the error is still null whatever the controller i tries to access without the `access-token`, although i have decorated them with `[Authorize]` or `[Authorize(AuthenticationSchemes = OpenIddictValidationDefaults.AuthenticationScheme)]` – User_4373 Dec 26 '19 at 07:27
  • isn't there any event that hits only when the authorization failed when the controller has `[Authorize(AuthenticationSchemes = OpenIddictValidationDefaults.AuthenticationScheme)]` the attriibute. – User_4373 Dec 26 '19 at 07:39

1 Answers1

1

Just a code sample if using AddJwtBearer to customize the error in response :

services.AddAuthentication("myschema")
.AddJwtBearer("myschema", options =>
{
    options.Authority = "http://localhost:54540/";
    options.Audience = "resource_server";
    options.RequireHttpsMetadata = false;
    options.Events = new JwtBearerEvents();

    options.Events.OnChallenge = context =>
    {
        // Skip the default logic.
        context.HandleResponse();
        if (string.IsNullOrEmpty(context.HttpContext.Request.Headers["Authorization"]))
        {
            var payload = new JObject
            {
                ["error"] = "No token",
                ["error_description"] = "No token",
            };
            return context.Response.WriteAsync(payload.ToString());
        }

        else
        {
            var payload = new JObject
            {
                ["error"] = context.Error,
                ["error_description"] = context.ErrorDescription,
                ["error_uri"] = context.ErrorUri
            };
            return context.Response.WriteAsync(payload.ToString());
        }

    };
});

And apply in your action :

[Authorize(AuthenticationSchemes = "myschema")]
[HttpGet("message")]
public async Task<IActionResult> GetMessage()
{
    var user = await _userManager.GetUserAsync(User);
    if (user == null)
    {
        return BadRequest();
    }

    return Content($"{user.UserName} has been successfully authenticated.");
}
Nan Yu
  • 26,101
  • 9
  • 68
  • 148
  • does the same thing can be done using the openiddict event handler? i tried using `IOpenIddictValidationEventHandler` but could not implement properly. – User_4373 Dec 26 '19 at 08:21
  • i haven't tried that. you can firstly check AddJwtBearer extension – Nan Yu Dec 26 '19 at 08:25
  • yeah i have tried that and it worked in api request! but that effect my client side login which uses the implicit flow. and it is giving no token error everywhere now and also do i need to change every authorize attribute to use with the schema `myschema`? – User_4373 Dec 26 '19 at 08:31
  • try `services.AddMvc(options => { var defaultPolicy = new AuthorizationPolicyBuilder() .AddAuthenticationSchemes("myschema") .RequireAuthenticatedUser() .Build(); options.Filters.Add(new AuthorizeFilter(defaultPolicy)); });` – Nan Yu Dec 26 '19 at 08:38
  • But if your protected resource is in AuthorizationServer app, i suggest you apply authorize attribute on controllers/actions instead apply global fliter . – Nan Yu Dec 26 '19 at 08:40
  • yes i have protected resource in Authorization Server app, and also i have two flow in same server i.e. `implicit` and `client-credentials`. also using that global filter won't redirect to login page now, and it redirected to 401 pages! – User_4373 Dec 26 '19 at 08:42
  • so that is what i said ` if your protected resource is in AuthorizationServer app, i suggest you apply authorize attribute on controllers/actions instead apply global fliter .` . You still need OIDC authecation schema for login . – Nan Yu Dec 26 '19 at 08:44
  • yes i was using `[Authorize(AuthenticationSchemes = OAuthValidationDefaults.AuthenticationScheme)] for `EnableUserinfoEndpoint("/api/userinfo");` it is getting error now. error is `System.InvalidOperationException: No authentication handler is configured to authenticate for the scheme: Bearer` – User_4373 Dec 26 '19 at 08:48
  • not sure what is your scenario , just put `[Authorize(AuthenticationSchemes = "myschema")]` on the protected controllers which need bearer authecation , don't need to change anything others – Nan Yu Dec 26 '19 at 08:51
  • now I am getting this error ` [Error] An unhandled exception has occurred while executing the request System.InvalidOperationException: IDX20803: Unable to obtain configuration from: '[PII is hidden by default. Set the 'ShowPII' flag in IdentityModelEventSource.cs to true to reveal it.]'. at Microsoft.IdentityModel.Protocols.ConfigurationManager`1.d__24.MoveNext()` – User_4373 Dec 26 '19 at 08:54
  • not sure what is your current code , you don't need to delete `AddValidation` ,just keep the orginal codes and add new authecation schema , and apply athorize attribute on the needed actions . – Nan Yu Dec 26 '19 at 08:58
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/204867/discussion-between-user-4373-and-nan-yu). – User_4373 Dec 26 '19 at 09:00