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!!!
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.
Most helpful comment
@weitzhandler, I've used following workaround:
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.