Openiddict-core: Should return 401 but is returning 404

Created on 19 Jan 2017  路  5Comments  路  Source: openiddict/openiddict-core

Hi!

I'm trying to test my web api when user has an invalid access token, but instead of 401, I'm getting an 404 result.

Here is my startup

public class Startup
    {
        private readonly IHostingEnvironment _environment;

        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();

            _environment = env;
        }

        public IConfigurationRoot Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton(Configuration);

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

            services.AddAuthorization();

            // Register the OpenIddict services.
            // Note: use the generic overload if you need
            // to replace the default OpenIddict entities.
            services.AddOpenIddict()
                // Register the Entity Framework stores.
                .AddEntityFrameworkCoreStores<ApplicationDbContext>()

                // Register the ASP.NET Core MVC binder used by OpenIddict.
                // Note: if you don't call this method, you won't be able to
                // bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
                .AddMvcBinders()

                // .UseJsonWebTokens()

                // Enable the token endpoint (required to use the password flow).
                .EnableTokenEndpoint("/connect/token")
                .EnableRevocationEndpoint("/connect/logout")

                // Allow client applications to use the grant_type=password flow.
                .AllowPasswordFlow()
                .AllowRefreshTokenFlow()

                // During development, you can disable the HTTPS requirement.
                .DisableHttpsRequirement()

                .SetAccessTokenLifetime(TimeSpan.FromHours(5))
                .SetRefreshTokenLifetime(TimeSpan.FromHours(10))

                // Register a new ephemeral key, that is discarded when the application
                // shuts down. Tokens signed using this key are automatically invalidated.
                // This method should only be used during development.
                .AddEphemeralSigningKey();

            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

            var iocContainer = new ApplicationIoCContainer(services, Configuration);
            iocContainer.RegisterModules();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            app.UseCors(builder =>
            // This will allow any request from any server. Tweak to fit your needs!
            // The fluent API is pretty pleasant to work with.
            builder.AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowAnyOrigin()
            );

            app.UseIdentity();

            app.UseOAuthValidation();

            app.UseOpenIddict();

            app.UseMvc();

            // Chamar apenas quando for criar as tabelas do banco
            InitializeAsync(app.ApplicationServices, CancellationToken.None).GetAwaiter().GetResult();
        }

        private async Task InitializeAsync(IServiceProvider services, CancellationToken cancellationToken)
        {
            // Create a new service scope to ensure the database context is correctly disposed when this methods returns.
            using (var scope = services.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
                // ReSharper disable once MethodSupportsCancellation
                await context.Database.EnsureCreatedAsync();

                var manager = scope.ServiceProvider.GetRequiredService<OpenIddictApplicationManager<OpenIddictApplication>>();

                if (await manager.FindByClientIdAsync("ClientId", cancellationToken) == null)
                {
                    var application = new OpenIddictApplication
                    {
                        ClientId = "ClientId",
                        DisplayName = "Client name",

                        // I guess my error is here... but don't know how to resolve with an Api
                        LogoutRedirectUri = "http://localhost:5000/signout-oidc",
                        RedirectUri = "http://localhost:5000/signin-oidc"
                    };

                    await manager.CreateAsync(application, cancellationToken);
                }
            }
        }
    }

and this is the generated log

info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:5000/api/program/get-paged?page=1&pagesize=100
info: AspNet.Security.OAuth.Validation.OAuthValidationMiddleware[7]
      Bearer was not authenticated. Failure message: Authentication failed because the access token was invalid.
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
      Authorization failed for user: (null).
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
info: Microsoft.AspNetCore.Mvc.ChallengeResult[1]
      Executing ChallengeResult with authentication schemes ().
info: AspNet.Security.OAuth.Validation.OAuthValidationMiddleware[12]
      AuthenticationScheme: Bearer was challenged.
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware[12]
      AuthenticationScheme: Identity.Application was challenged.
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
      Executed action Life.Platform.Api.Controllers.ProgramController.Get (Life.Platform.Api) in 55.8041ms
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 60.6096ms 302
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:5000/Account/Login?ReturnUrl=%2Fapi%2Fprogram%2Fget-paged%3Fpage%3D1%26pagesize%3D100
info: AspNet.Security.OAuth.Validation.OAuthValidationMiddleware[7]
      Bearer was not authenticated. Failure message: Authentication failed because the access token was invalid.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 5.0739ms 404

Looks like it's using CookieMiddlware, but why?
And, how can I just return a 401 result, when the user is unautenticated and 403 when user is unauthorized?

Thanks for your help!
And congrats... This is a big tool!!!

question

Most helpful comment

@weitzhandler, I've used following workaround:

        services.ConfigureApplicationCookie(options =>
        {
            options.Events.OnRedirectToLogin = context =>
            {
                context.Response.StatusCode = 401;
                return Task.CompletedTask;
            };
        });

If you call this in ConfigureServices method, it sets up the cookie to return 401 on redirection to login. I think this won't be a good solution if you use this redirection somewhere else in a code. Otherwise - should work.

All 5 comments

And congrats... This is a big tool!!!

Glad you like it :smile:

The behavior you're seeing is caused by Identity, that replaces 401 responses by 302 responses. In your case, the login endpoint doesn't exist, so you're getting a 404 response.

If you're not using cookies authentication, consider removing app.UseIdentity();.

Hi, thanks for your response.

It works perfectly!!

One more time, congratulations!!!

@PinpointTownes - removing app.UseIdentity(); gives an error.

Any solution for ASP.NET Core 2.0 where app.UseAuthentication cannot be removed?

@weitzhandler, I've used following workaround:

        services.ConfigureApplicationCookie(options =>
        {
            options.Events.OnRedirectToLogin = context =>
            {
                context.Response.StatusCode = 401;
                return Task.CompletedTask;
            };
        });

If you call this in ConfigureServices method, it sets up the cookie to return 401 on redirection to login. I think this won't be a good solution if you use this redirection somewhere else in a code. Otherwise - should work.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kevinchalet picture kevinchalet  路  19Comments

kevinchalet picture kevinchalet  路  40Comments

kevinchalet picture kevinchalet  路  20Comments

torfikarl picture torfikarl  路  14Comments

singlewind picture singlewind  路  14Comments