ASP.NET Core 2 API Call Redirected (302)

I am trying to migrate this project https://github.com/asadsahi/AspNetCoreSpa from .net core 1.1 to 2.0, but you have a problem after successfully logging in. After logging in, my GET api calls e. d. to https: // localhost: 44331 / api / profile / test ends with a redirect (302), and I don't know why. I got a carrier token and it looks fine.

Request Header Format: Authorization: Media [Token]

[Route("api/[controller]")] public class ProfileController : BaseController { private readonly UserManager<ApplicationUser> _userManager; private readonly ILogger _logger; public ProfileController(ILoggerFactory loggerFactory, UserManager<ApplicationUser> userManager) { _logger = loggerFactory.CreateLogger<ProfileController>(); _userManager = userManager; } [HttpGet("test")] public async Task<IActionResult> Test() { return Json(ModelState.GetModelErrors()); } } [Authorize] [ServiceFilter(typeof(ApiExceptionFilter))] [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)] public class BaseController : Controller { public BaseController() { } } 

Startup.cs:

 public void ConfigureServices(IServiceCollection services) { if (_hostingEnv.IsDevelopment()) { services.AddSslCertificate(_hostingEnv); } else { services.Configure<MvcOptions>(o => o.Filters.Add(new RequireHttpsAttribute())); } services.AddOptions(); services.AddCors(); services.AddLogging(); services.AddResponseCompression(options => { options.MimeTypes = Helpers.DefaultMimeTypes; }); services.AddAuthentication(sharedOptions => { sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; sharedOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; sharedOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(cfg => { cfg.SaveToken = true; cfg.TokenValidationParameters = new TokenValidationParameters { ValidIssuer = Configuration["Authentication:BearerTokens:Issuer"], ValidAudience = Configuration["Authentication:BearerTokens:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Authentication:BearerTokens:Key"])), ValidateIssuerSigningKey = false, ValidateLifetime = true, ClockSkew = TimeSpan.Zero }; cfg.Events = new JwtBearerEvents { OnAuthenticationFailed = context => { var logger = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>().CreateLogger(nameof(JwtBearerEvents)); logger.LogError("Authentication failed.", context.Exception); return Task.CompletedTask; }, OnMessageReceived = context => { return Task.CompletedTask; }, OnChallenge = context => { var logger = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>().CreateLogger(nameof(JwtBearerEvents)); logger.LogError("OnChallenge error", context.Error, context.ErrorDescription); return Task.CompletedTask; } }; }); services.AddDbContext<ApplicationDbContext>(options => { string useSqLite = Startup.Configuration["Data:useSqLite"]; if (useSqLite.ToLower() == "true") { options.UseSqlite(Startup.Configuration["Data:SqlLiteConnectionString"]); } else { options.UseSqlServer(Startup.Configuration["Data:SqlServerConnectionString"]); } options.UseOpenIddict(); }); services.AddIdentity<ApplicationUser, ApplicationRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); //services.ConfigureApplicationCookie(options => //{ // options.LoginPath = "/login"; // options.Events.OnRedirectToLogin = context => // { // if (context.Request.Path.StartsWithSegments("/api") && // context.Response.StatusCode == (int)HttpStatusCode.OK) // { // context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; // } // else // { // context.Response.Redirect(context.RedirectUri); // } // return Task.FromResult(0); // }; //}); services.AddOAuthProviders(); services.AddCustomOpenIddict(); services.AddMemoryCache(); services.RegisterCustomServices(); services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN"); services.AddCustomizedMvc(); // Node services are to execute any arbitrary nodejs code from .net services.AddNodeServices(); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new Info { Title = "AspNetCoreSpa", Version = "v1" }); }); } public void Configure(IApplicationBuilder app) { app.AddDevMiddlewares(); if (_hostingEnv.IsProduction()) { app.UseResponseCompression(); } app.SetupMigrations(); app.UseXsrf(); app.UseStaticFiles(); app.UseAuthentication(); app.UseMvc(routes => { // http://stackoverflow.com/questions/25982095/using-googleoauth2authenticationoptions-got-a-redirect-uri-mismatch-error routes.MapRoute(name: "signin-google", template: "signin-google", defaults: new { controller = "Account", action = "ExternalLoginCallback" }); routes.MapSpaFallbackRoute( name: "spa-fallback", defaults: new { controller = "Home", action = "Index" }); }); } 

My IServiceCollection extensions:

 public static IServiceCollection AddCustomizedMvc(this IServiceCollection services) { services.AddMvc(options => { options.Filters.Add(typeof(ModelValidationFilter)); }) .AddJsonOptions(options => { options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; }); return services; } public static IServiceCollection AddOAuthProviders(this IServiceCollection services) { services.AddAuthentication() .AddFacebook(o => { o.AppId = Startup.Configuration["Authentication:Facebook:AppId"]; o.AppSecret = Startup.Configuration["Authentication:Facebook:AppSecret"]; }); services.AddAuthentication() .AddGoogle(o => { o.ClientId = Startup.Configuration["Authentication:Google:ClientId"]; o.ClientSecret = Startup.Configuration["Authentication:Google:ClientSecret"]; }); services.AddAuthentication() .AddTwitter(o => { o.ConsumerKey = Startup.Configuration["Authentication:Twitter:ConsumerKey"]; o.ConsumerSecret = Startup.Configuration["Authentication:Twitter:ConsumerSecret"]; }); services.AddAuthentication() .AddMicrosoftAccount(o => { o.ClientId= Startup.Configuration["Authentication:Microsoft:ClientId"]; o.ClientSecret = Startup.Configuration["Authentication:Microsoft:ClientSecret"]; }); return services; } public static IServiceCollection AddCustomOpenIddict(this IServiceCollection services) { // Configure Identity to use the same JWT claims as OpenIddict instead // of the legacy WS-Federation claims it uses by default (ClaimTypes), // which saves you from doing the mapping in your authorization controller. services.Configure<IdentityOptions>(options => { options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name; options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject; options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role; }); // Register the OpenIddict services. 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() // Enable the token endpoint. .EnableTokenEndpoint("/connect/token") // Enable the password and the refresh token flows. .AllowPasswordFlow() .AllowRefreshTokenFlow() // During development, you can disable the HTTPS requirement. .DisableHttpsRequirement() // 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(); // On production, using a X.509 certificate stored in the machine store is recommended. // You can generate a self-signed certificate using Pluralsight self-cert utility: // https://s3.amazonaws.com/pluralsight-free/keith-brown/samples/SelfCert.zip // // services.AddOpenIddict() // .AddSigningCertificate("7D2A741FE34CC2C7369237A5F2078988E17A6A75"); // // Alternatively, you can also store the certificate as an embedded .pfx resource // directly in this assembly or in a file published alongside this project: // // services.AddOpenIddict() // .AddSigningCertificate( // assembly: typeof(Startup).GetTypeInfo().Assembly, // resource: "AuthorizationServer.Certificate.pfx", // password: "OpenIddict"); return services; } public static IServiceCollection AddCustomDbContext(this IServiceCollection services) { // Add framework services. return services; } public static IServiceCollection RegisterCustomServices(this IServiceCollection services) { // New instance every time, only configuration class needs so its ok services.Configure<SmsSettings>(options => Startup.Configuration.GetSection("SmsSettingsTwillio").Bind(options)); services.AddTransient<UserResolverService>(); services.AddTransient<IEmailSender, EmailSender>(); services.AddTransient<ISmsSender, SmsSender>(); services.AddScoped<ApiExceptionFilter>(); return services; } 

Here are my packages:

 <ItemGroup> <PackageReference Include="AspNet.Security.OAuth.Introspection" Version="2.0.0-*" /> <PackageReference Include="AspNet.Security.OAuth.Validation" Version="2.0.0-*" /> <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.AzureAppServicesIntegration" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Cors" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Antiforgery" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.Facebook" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.Twitter" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Https" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="2.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.AngularServices" Version="1.1.0-beta-000002" /> <PackageReference Include="AspNet.Security.OAuth.GitHub" Version="1.0.0-beta3-final" /> <PackageReference Include="AspNet.Security.OAuth.LinkedIn" Version="1.0.0-beta3-final" /> <PackageReference Include="OpenIddict" Version="2.0.0-*" /> <PackageReference Include="OpenIddict.EntityFrameworkCore" Version="2.0.0-*" /> <PackageReference Include="OpenIddict.Mvc" Version="2.0.0-*" /> <PackageReference Include="SendGrid" Version="9.9.0" /> <PackageReference Include="MailKit" Version="1.18.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" /> <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="1.0.0" /> <PackageReference Include="Twilio" Version="5.6.3" /> <PackageReference Include="Stripe.net" Version="10.4.0" /> <PackageReference Include="Newtonsoft.Json" Version="10.0.3" /> <PackageReference Include="Webpack" Version="4.0.0" /> <PackageReference Include="Serilog" Version="2.5.0" /> <PackageReference Include="Serilog.Extensions.Logging" Version="2.0.2" /> <PackageReference Include="Serilog.Sinks.Seq" Version="3.3.3" /> <PackageReference Include="Bogus" Version="17.0.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0"> <PrivateAssets>All</PrivateAssets> </PackageReference> <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.0.0"> <PrivateAssets>All</PrivateAssets> </PackageReference> <PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.ViewCompilation" Version="2.0.0" PrivateAssets="All" /> </ItemGroup> <ItemGroup> <DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="2.0.0" /> <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" /> <DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="2.0.0" /> <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" /> </ItemGroup> 

Here are my logs:

 info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Request starting HTTP/1.1 GET http://localhost:44331/api/profile/test application/json; charset=UTF-8 info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] Authorization failed for user: (null). info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] Authorization failed for user: (null). info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3] Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'. info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3] Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'. info: Microsoft.AspNetCore.Mvc.ChallengeResult[1] Executing ChallengeResult with authentication schemes (). info: Microsoft.AspNetCore.Mvc.ChallengeResult[1] Executing ChallengeResult with authentication schemes (). info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[12] AuthenticationScheme: Identity.Application was challenged. info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[12] AuthenticationScheme: Identity.Application was challenged. info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] Executed action AspNetCoreSpa.Server.Controllers.api.ProfileController.Test (AspNetCoreSpa) in 43.3105ms info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] Executed action AspNetCoreSpa.Server.Controllers.api.ProfileController.Test (AspNetCoreSpa) in 43.3105ms info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 67.4133ms 302 infoinfo: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 67.4133ms 302 : Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Request starting HTTP/1.1 GET http://localhost:44331/Account/Login? ReturnUrl=%2Fapi%2Fprofile%2Ftest application/json; charset=UTF-8 info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Request starting HTTP/1.1 GET http://localhost:44331/Account/Login? ReturnUrl=%2Fapi%2Fprofile%2Ftest application/json; charset=UTF-8 info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] Executing action method AspNetCoreSpa.Server.Controllers.HomeController.Index (AspNetCoreSpa) with arguments ((null)) - ModelState is Valid info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] Executing action method AspNetCoreSpa.Server.Controllers.HomeController.Index (AspNetCoreSpa) with arguments ((null)) - ModelState is Valid info: Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ViewResultExecutor[1] Executing ViewResult, running view at path /Views/Home/Index.cshtml. info: Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ViewResultExecutor[1] Executing ViewResult, running view at path /Views/Home/Index.cshtml. info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] Executed action AspNetCoreSpa.Server.Controllers.HomeController.Index (AspNetCoreSpa) in 13.2746ms info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] Executed action AspNetCoreSpa.Server.Controllers.HomeController.Index (AspNetCoreSpa) in 13.2746ms info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 79.2352ms 200 text/html; charset=utf-8 info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 79.2352ms 200 text/html; charset=utf-8 

Interesting about the following line:

User authorization failed: (null)

It has already been found that the ASP.NET Kernel authorization failed for the user: (null) but there is no answer yet, and I think this is a .net core 1 kernel problem.

Thank you for your help!

Regards Simon

+5
source share
3 answers

I ran into the same problem, and to solve the problem I had to include the authentication scheme in the Authorize attribute on the controller.

In your case:

 [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] [ServiceFilter(typeof(ApiExceptionFilter))] [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)] public class BaseController : Controller { public BaseController() { } } 
+1
source

For me this was due to Cross-Origin Resource Sharing (CORS). I have an API in an application service in Azure with CORS enabled. I got 302 to disappear when I added the calling APIs to the list of allowed sources.

I had to do this even if I already added this origin to my aspnet kernel startup code.

0
source

When you call AddIdentity, it adds Cookie authentication, which overrides your intended JWT bearer authentication. One way around this is to move the AddIdentity call before setting up JWT authentication. Below is the code that works for me:

 // setup identity services.AddIdentity<ApplicationUser, ApplicationRole>() .AddEntityFrameworkStores<MyMoneyDbContext>() .AddDefaultTokenProviders(); // setup Jwt authentication services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, jwtBearerOptions => { jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, ... 

Another alternative is to use AddIdentityCore, but I have never tried this.

0
source

Source: https://habr.com/ru/post/1271496/


All Articles