3

I am developing a web-api which uses some authorization against another of our applications to verify that a user is still logged in and may continue with the action they've requested. But the server responds with a 500 internal server error when the user is not Authenticated, stack trace can be found below. I seem to be unable to wrestle the configuration into a correct state. From the stack trace, I can tell that it tries to fall back to some default authentication scheme, which I haven't defined and ideally aren't interested in as we always wish to authorize against the 3rd party instead and simply fail if it returns false.

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IAuthService>(_ =>
        new AuthService(damInfo.BaseUrl, damInfo.SystemUsername, damInfo.SystemPassword));
    services.AddScoped<IAuthorizationHandler, AuthenticationHandler>();

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    services.AddAuthorization(options =>
    {
        options.AddPolicy("ServicePolicy", policy =>
            policy.Requirements.Add(new ServiceRequirement("")));
        /*options.DefaultPolicy = new AuthorizationPolicyBuilder().AddAuthenticationSchemes(Constants.NoOp).RequireAuthenticatedUser().Build(); // keep#1#*/
        options.InvokeHandlersAfterFailure = false;
    });

    services.AddAuthentication(options =>
    {
        options.DefaultScheme = Constants.NoOp;
        options.DefaultChallengeScheme = Constants.NoOp;
    });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
        app.UseDeveloperExceptionPage();
    else
        app.UseHsts();

    app.UseCors(builder =>
    {
        builder.AllowAnyHeader();
        builder.AllowAnyMethod();
        builder.AllowAnyOrigin();
    });

    app.UseHttpsRedirection();
    app.UseMvc();
}

Policy:

using Microsoft.AspNetCore.Authorization;

namespace Core.Services.Authentication
{
    public class ServiceRequirement : IAuthorizationRequirement
    {
        public string CurrentToken { get; private set; }

        public ServiceRequirement(string token)
        {
            CurrentToken = token;
        }       
    }
}

Authorize attribute:

[Authorize(Policy = "ServicePolicy")]

Auth handler:

using System.Net.Http.Headers;
using System.Threading.Tasks;
using Digizuite.Services.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;

namespace Core.Services.Authentication
{
    public class AuthenticationHandler : AuthorizationHandler<ServiceRequirement>
    {
        readonly IAuthService _authService;

        /// <summary>
        /// Authentication handler that check if user is logged in
        /// </summary>
        /// <param name="authService"></param>
        public AuthenticationHandler(IAuthService authService)
        {
            _authService = authService;
        }

        /// <summary>
        /// Handle requirement and check isLoggedIn
        /// </summary>
        /// <param name="context"></param>
        /// <param name="requirement"></param>
        /// <returns></returns>
        protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context,
            ServiceRequirement requirement)
        {
            var authFilterCtx = (Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext)context.Resource;
            var httpContext = authFilterCtx.HttpContext;

            var headers = httpContext.Request.Headers[HeaderNames.Authorization];

            if (StringValues.IsNullOrEmpty(headers) == true)
            {
                context.Fail();
            }
            else
            {
                var auth = AuthenticationHeaderValue.Parse(headers);
                var loginResponse = _authService.IsLoggedInRequest(auth.Parameter).Result;
                if (loginResponse.IsLoggedIn == false)
                {
                    context.Fail();
                }
                else
                {
                    context.Succeed(requirement);
                }
            }
        }
    }
}

StackTrace:

fail: Microsoft.AspNetCore.Server.Kestrel[13]
      Connection id "0HLOC48NR5MP4", Request id "0HLOC48NR5MP4:00000001": An unhandled exception was thrown by the application.
System.InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found.
   at Microsoft.AspNetCore.Authentication.AuthenticationService.ChallengeAsync(HttpContext context, String scheme, AuthenticationProperties properties)
   at Microsoft.AspNetCore.Mvc.ChallengeResult.ExecuteResultAsync(ActionContext context)
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultAsync(IActionResult result)
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAlwaysRunResultFilters()
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
   at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Digizuite.Logging2.LogClient.LoggerRequestMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) in C:\dev\digizuite.core\Libs\LogClient\LoggerRequestMiddleware.cs:line 40
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass5_1.<<UseMiddlewareInterface>b__1>d.MoveNext()

Kirk Larkin
  • 84,915
  • 16
  • 214
  • 203
  • 1
    [This question](https://stackoverflow.com/questions/52008000/how-to-correctly-setup-policy-authorization-for-web-api-in-net-core/52042420) (which I [answered](https://stackoverflow.com/a/52042420/2630078)), sounds similar enough that it might be worth checking out. – Kirk Larkin Jul 19 '19 at 09:41

0 Answers0