Search code examples
openiddictasp.net-core-2.0

.Net Core 2.0 Web API OpenIddict Authorization: redirecting to index instead of returning json data


So, the problem is that when I use the AuthorizeAttribute on top of my api controller, it stops working the expected way.

When I call a getAllUsers action, instead of returning the users in json format, the Identity somehow redirects to index.html and then I get a json parser error in my Angular client app, because html is not valid json data that can be parsed.

This started to happen after upgrading to Asp.Net Core 2.0.

I think that perhaps I have to change something in my Startup.cs or Program.cs. But I can't figure out what.

I have followed the Refresh Token Sample on OpenIddict for the new Core 2.0, and everything seems to be ok.

So here is my code...

Startup.cs

public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<ApplicationDbContext>(options => {
            options.UseSqlServer(Configuration.GetConnectionString("LocalDB"))
                .UseOpenIddict();
        });
        services.AddScoped<IUserRepository, UserRepository>();
        services.AddScoped<IRoleRepository, RoleRepository>();
        services.AddScoped<IManadRepository, ManadRepository>();
        services.AddScoped<IManadRubricaRepository, ManadRubricaRepository>();
        services.AddScoped<IManadSistemaRepository, ManadSistemaRepository>();
        services.AddScoped<IRestituicaoRepository, RestituicaoRepository>();
        services.AddTransient<ApplicationDbSeedData>();

        services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
            {
                options.User.RequireUniqueEmail = true;
                options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
                options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
                options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
            })
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();

        services.AddOpenIddict(options =>
        {
            options.AddEntityFrameworkCoreStores<ApplicationDbContext>();
            options.AddMvcBinders();
            options.EnableTokenEndpoint("/connect/token");
            options.AllowPasswordFlow();
            options.AllowRefreshTokenFlow();

            if (!_env.IsProduction())
                options.DisableHttpsRequirement();
        });

        // Add framework services.
        services.AddMvc();

        services.AddAuthentication()
            .AddOAuthValidation();

        services.AddAuthorization();

        services.AddTransient<IMailSender, MailjetSender>();

        services.AddScoped<IManadParser, ManadParser>();
    }

public void Configure(IApplicationBuilder app, ApplicationDbSeedData dbDataSeeder)
    {        
        if (_env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
            {
                HotModuleReplacement = true
            });
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        Mapper.Initialize(cfg =>
        {
            cfg.AddProfile<AutoMapperProfile>();
        });

        app.UseStaticFiles();

        app.UseAuthentication();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");

            routes.MapSpaFallbackRoute(
                name: "spa-fallback",
                defaults: new { controller = "Home", action = "Index" });
        });

        dbDataSeeder.EnsureSeedData().Wait();
    }

UsersController.cs

[Route("api/[controller]")]
[Authorize]
public class UsersController : Controller
{
    [HttpGet]
    [Authorize(Roles = "Administrador")]
    public IActionResult GetAllUsers()
    {
        try
        {
            var result = _repository.GetAllUsers();  

            return Ok(result);
        }
        catch (Exception ex)
        {
            _logger.LogError($"Failed to get all users: {ex}");

            return BadRequest(ex.Message);
        }
    }
}

If I put a breakpoint in the GetAllUsers method, it never gets hitted. Somehow because of authorization, the application redirects to index.html before.

Program.cs

public class Program
{
    public static void Main(string[] args)
    {
        BuildWebHost(args).Run();
    }

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .Build();
}

By the way, authentication is working. I am able to get the tokens, but unable to authorize the controller access.


Solution

  • Solved it. Just needed some bit of configuration just like I thought. Just add DefaultAuthenticateScheme option like this:

    services.AddAuthentication(options => options.DefaultAuthenticateScheme = OAuthValidationDefaults.AuthenticationScheme)
                .AddOAuthValidation();
    

    After adding this, the controller started to work correctly, resulting json data and not index.html.