title: JWT Authorization Redirects to Login
body: <p>I have developed a Core 2 MVC app with Individual User Accounts using standard Identity. The MVC app is all working fine.</p>
  
  <p>I am trying to add a public API and authorize requests using JWT.</p>
  
  <p>However, something is going wrong when it is trying to authorize the user. When I submit a request to the controller, it redirects me to my Login page, so the response body is HTML.</p>
  
  <p>I need to to authorize and return data (or not if the token is invalid).</p>
  
  <p>What have I done wrong? The token generation is all OK.</p>
  
  <p><strong>Token Controller</strong></p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Threading.Tasks;
  using Microsoft.AspNetCore.Http;
  using Microsoft.AspNetCore.Mvc;
  using Microsoft.AspNetCore.Authorization;
  using TechsportiseOnline.Models.AccountViewModels;
  using Microsoft.AspNetCore.Identity;
  using TechsportiseOnline.Models;
  using System.Security.Claims;
  using System.IdentityModel.Tokens.Jwt;
  using Microsoft.IdentityModel.Tokens;
  using System.Text;
  using Microsoft.Extensions.Configuration;
  using Microsoft.Extensions.Options;
  using TechsportiseOnline.Helpers;
  
  namespace TechsportiseOnline.Controllers
  {
      [Produces("application/json")]
      [Route("api/Token")]
      public class TokenController : Controller
      {
          private readonly UserManager&lt;ApplicationUser&gt; _userManager;
          private readonly SignInManager&lt;ApplicationUser&gt; _signInManager;
          private readonly IConfiguration _configuration;
          private readonly IOptions&lt;JWTSettings&gt; _jwtConfig;
  
          public TokenController(
                UserManager&lt;ApplicationUser&gt; userManager,
                SignInManager&lt;ApplicationUser&gt; signInManager,
                IConfiguration configuration,
                IOptions&lt;JWTSettings&gt; jwtConfig)
                      {
                          _userManager = userManager;
                          _signInManager = signInManager;
                          _configuration = configuration;
                          _jwtConfig = jwtConfig;
  
  
          }
  
          [AllowAnonymous]
          [HttpPost]
          public async Task&lt;IActionResult&gt; GenerateToken([FromBody] LoginViewModel model)
          {
              if (ModelState.IsValid)
              {
                  var user = await _userManager.FindByEmailAsync(model.Email);
  
                  if (user != null)
                  {
                      var result = await _signInManager.CheckPasswordSignInAsync(user, model.Password, false);
                      if (result.Succeeded)
                      {
  
                          var claims = new[]
                          {
                            new Claim(JwtRegisteredClaimNames.Sub, user.Email),
                            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                          };
  
                          var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtConfig.Value.SecretKey.ToString()));
                          var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
  
                          var token = new JwtSecurityToken(_jwtConfig.Value.Issuer.ToString(),
                            _jwtConfig.Value.Audience.ToString(),
                            claims,
                            expires: DateTime.Now.AddDays(30),
                            signingCredentials: creds);
  
                          return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
                      }
                  }
              }
  
              return BadRequest("Could not create token");
          }
      }
  }
  </code></pre>
  
  <p><strong>Startup.cs</strong></p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Threading.Tasks;
  using Microsoft.AspNetCore.Builder;
  using Microsoft.AspNetCore.Identity;
  using Microsoft.EntityFrameworkCore;
  using Microsoft.AspNetCore.Hosting;
  using Microsoft.Extensions.Configuration;
  using Microsoft.Extensions.DependencyInjection;
  using TechsportiseOnline.Data;
  using TechsportiseOnline.Models;
  using TechsportiseOnline.Services;
  using Microsoft.AspNetCore.Mvc;
  using Microsoft.AspNetCore.Authorization;
  using Microsoft.AspNetCore.Mvc.Authorization;
  using TechsportiseOnline.Authorization;
  using TechsportiseOnline.Helpers;
  using Swashbuckle.AspNetCore.Swagger;
  using System.IO;
  using Microsoft.Extensions.PlatformAbstractions;
  using static TechsportiseOnline.Helpers.Swagger;
  using Microsoft.AspNetCore.Authentication.JwtBearer;
  using Microsoft.IdentityModel.Tokens;
  using System.Text;
  
  namespace TechsportiseOnline
  {
      public class Startup
      {
          public Startup(IConfiguration configuration)
          {
              Configuration = configuration;
          }
  
          public IConfiguration Configuration { get; }
  
          // This method gets called by the runtime. Use this method to add services to the container.
          public void ConfigureServices(IServiceCollection services)
          {
              services.AddDbContext&lt;ApplicationDbContext&gt;(options =&gt;
                  options.UseSqlServer(Configuration.GetConnectionString("TechsportiseDB")));
              //options.UseInMemoryDatabase("Teschsportise"));
  
  
              services.Configure&lt;JWTSettings&gt;(Configuration);
  
              services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                        .AddJwtBearer(cfg =&gt;
                        {
                            cfg.IncludeErrorDetails = true;
                            cfg.RequireHttpsMetadata = false;
                            cfg.SaveToken = true;
  
                            var secretKey = Configuration.GetSection("JWTSettings.SecretKey").Value;
                            var issuer = Configuration.GetSection("JWTSettings.Issuer").Value;
                            var audience = Configuration.GetSection("JWTSettings.Audience").Value;
                            var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));
  
                            cfg.TokenValidationParameters = new TokenValidationParameters()
                            {
                                ValidateIssuer = true,
                                ValidIssuer = Configuration.GetSection("JWTSettings.Issuer").Value,
                                ValidateAudience = true,
                                ValidAudience = Configuration.GetSection("JWTSettings.Audience").Value,
                                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWTSettings:SecretKey"]))
                            };
  
                        });
  
              services.Configure&lt;JWTSettings&gt;(Configuration.GetSection("JWTSettings"));
  
              services.AddIdentity&lt;ApplicationUser, IdentityRole&gt;(config =&gt;
                  {
                      config.SignIn.RequireConfirmedEmail = true;
                  })
                  .AddEntityFrameworkStores&lt;ApplicationDbContext&gt;()
                  .AddDefaultTokenProviders();
  
              services.Configure&lt;IdentityOptions&gt;(options =&gt;
              {
                  // Password settings
                  options.Password.RequireDigit = true;
                  options.Password.RequiredLength = 6;
                  options.Password.RequireNonAlphanumeric = false;
                  options.Password.RequireUppercase = false;
                  options.Password.RequireLowercase = false;
                  options.Password.RequiredUniqueChars = 2;
  
                  // Lockout settings
                  options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
                  options.Lockout.MaxFailedAccessAttempts = 10;
                  options.Lockout.AllowedForNewUsers = true;
  
                  // User settings
                  options.User.RequireUniqueEmail = true;
              });
  
              services.Configure&lt;AuthMessageSenderOptions&gt;(Configuration);
  
  
  
              services.ConfigureApplicationCookie(options =&gt;
              {
                  // Cookie settings
                  options.Cookie.HttpOnly = true;
                  options.Cookie.Expiration = TimeSpan.FromDays(150);
                  options.LoginPath = "/Account/Login"; // If the LoginPath is not set here, ASP.NET Core will default to /Account/Login
                  options.LogoutPath = "/Account/Logout"; // If the LogoutPath is not set here, ASP.NET Core will default to /Account/Logout
                  options.AccessDeniedPath = "/Account/AccessDenied"; // If the AccessDeniedPath is not set here, ASP.NET Core will default to /Account/AccessDenied
                  options.SlidingExpiration = true;
              });
  
              // Add application services.
              services.AddTransient&lt;IEmailSender, Email&gt;();
              //services.AddTransient&lt;ICreateContact&gt;();
              //services.AddTransient&lt;IUpdateContact&gt;();
  
              services.AddSwaggerGen(c =&gt;
              {
                  c.SwaggerDoc("v1", new Info { Title = "Techsportise API", Version = "v1" });
                  c.OperationFilter&lt;AddRequiredHeaderParameter&gt;();
                  var filePath = Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, "Techsportise.xml");
                  c.IncludeXmlComments(filePath);
              });
  
              services.AddMvc();
  
              var skipSSL = Configuration.GetValue&lt;bool&gt;("LocalTest:skipSSL");
              // requires using Microsoft.AspNetCore.Mvc;
              services.Configure&lt;MvcOptions&gt;(options =&gt;
              {
                  // Set LocalTest:skipSSL to true to skip SSL requrement in 
                  // debug mode. This is useful when not using Visual Studio.
                  if (!skipSSL)
                  {
                      options.Filters.Add(new RequireHttpsAttribute());
                  }
              });
  
  
              services.AddMvc(config =&gt;
              {
                  var policy = new AuthorizationPolicyBuilder()
                                   .RequireAuthenticatedUser()
                                   .Build();
                  config.Filters.Add(new AuthorizeFilter(policy));
              });
  
              services.AddScoped&lt;IAuthorizationHandler,
                        OwnerRaceAuthorizationHandler&gt;();
  
              services.AddSingleton&lt;IAuthorizationHandler,
                                    AdminRaceAuthorizationHandler&gt;();
  
              services.AddScoped&lt;IAuthorizationHandler,
                        OwnerRaceEntriesAuthorizationHandler&gt;();
  
              services.AddSingleton&lt;IAuthorizationHandler,
                                    AdminRaceEntriesAuthorizationHandler&gt;();
  
          }
  
          // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
          public void Configure(IApplicationBuilder app, IHostingEnvironment env)
          {
              if (env.IsDevelopment())
              {
                  app.UseDeveloperExceptionPage();
                  app.UseBrowserLink();
                  app.UseDatabaseErrorPage();
              }
              else
              {
                  app.UseExceptionHandler("/Home/Error");
              }
  
              app.UseStaticFiles();
  
              app.UseAuthentication();
  
              app.UseMvc(routes =&gt;
              {
                  routes.MapRoute(
                      name: "default",
                      template: "{controller=Home}/{action=Index}/{id?}");
              });
  
              // Enable middleware to serve generated Swagger as a JSON endpoint.
              app.UseSwagger();
  
              // Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint.
              app.UseSwaggerUI(c =&gt;
              {
                  c.SwaggerEndpoint("/swagger/v1/swagger.json", "Techsportise API V1");
              });
  
  
          }
      }
  }
  </code></pre>
  
  <p><strong>RacesController</strong></p>
  
  <pre><code>using Microsoft.AspNetCore.Authentication.JwtBearer;
  using Microsoft.AspNetCore.Authorization;
  using Microsoft.AspNetCore.Identity;
  using Microsoft.AspNetCore.Mvc;
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Threading.Tasks;
  using TechsportiseOnline.Data;
  using TechsportiseOnline.Helpers;
  using TechsportiseOnline.Models;
  
  namespace TechsportiseOnline.Controllers
  {
  
      /// &lt;summary&gt;
      /// This class is used as an API for Races
      /// &lt;/summary&gt;
      [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
      [Route("api/[controller]")]
      public class RaceController : Controller
      {
  
  
  
          private readonly ApplicationDbContext _context;
          private readonly IAuthorizationService _authorizationService;
          private readonly UserManager&lt;ApplicationUser&gt; _userManager;
  
          public RaceController(ApplicationDbContext context,
             IAuthorizationService authorizationService,
             UserManager&lt;ApplicationUser&gt; userManager)
          {
              _context = context;
              _userManager = userManager;
              _authorizationService = authorizationService;
          }
  
          /// &lt;summary&gt;
          /// Get all Races
          /// &lt;/summary&gt;
          /// &lt;remarks&gt;
          /// Gets all Races which have been created by the user
          /// &lt;/remarks&gt;
          /// &lt;returns&gt;All created Races&lt;/returns&gt;
          [HttpGet]
          public IEnumerable&lt;Race&gt; GetAll()
          {
              //Get only records where the OwnerId is not the logged in User.
              return _context.Races.Where(p =&gt; p.OwnerID == _userManager.GetUserId(User)).ToList();
          }
  
          /// &lt;summary&gt;
          /// Get a single Race
          /// &lt;/summary&gt;
          /// &lt;remarks&gt;
          /// Gets the details from a single Race from it's ID
          /// &lt;/remarks&gt;
          /// &lt;param name="id"&gt;Race ID&lt;/param&gt;
          /// &lt;returns&gt;Single Race&lt;/returns&gt;
          [HttpGet("{id}", Name = "GetRace")]
          public IActionResult GetById(long id)
          {
              //Only return the data when it is owned by the same Id
              var item = _context.Races.Where(p =&gt; p.OwnerID == _userManager.GetUserId(User)).FirstOrDefault(t =&gt; t.ID == id);
  
  
              if (item == null)
              {
                  return NotFound();
              }
  
              return new ObjectResult(item);
          }
  
          /// &lt;summary&gt;
          /// Get all entries for a Race
          /// &lt;/summary&gt;
          /// &lt;remarks&gt;
          /// Gets the all the entries from the race ID
          /// &lt;/remarks&gt;
          /// &lt;param name="id"&gt;Race ID&lt;/param&gt;
          /// &lt;returns&gt;All Entries from the given Race ID&lt;/returns&gt;
          [HttpGet("{id}/entries", Name = "GetEntriesByRaceID")]
          public IEnumerable&lt;RaceEntry&gt; GetAllEntries(long id)
          {
              //Only return the data when it is owned by the same Id
              //Get only records where the OwnerId is not the logged in User.
              return _context.RaceEntries.Where(p =&gt; p.OwnerID == _userManager.GetUserId(User))
                                          .Where(p =&gt; p.RaceID == id)
                                          .ToList();
          }
  
  
  
          ///// &lt;summary&gt;
          ///// Get all timings for a Race
          ///// &lt;/summary&gt;
          ///// &lt;remarks&gt;
          ///// Gets the all the timings from the race ID
          ///// &lt;/remarks&gt;
          ///// &lt;param name="id"&gt;Race ID&lt;/param&gt;
          ///// &lt;returns&gt;All timings from the given Race ID&lt;/returns&gt;
          //[HttpGet("{id}/timings", Name = "GetTimingsByRaceID")]
          //public IEnumerable&lt;Timing&gt; GetAllTimings(long id)
          //{
  
          //    //Only return the data when it is owned by the same Id
          //    //Get only records where the OwnerId is not the logged in User.
          //    return _context.Timings.Where(p =&gt; p.OwnerId == User.GetUserId())
          //                                .Where(p =&gt; p.RaceId == id)
          //                                .ToList();
          //}
  
          ///// &lt;summary&gt;
          ///// Get the results for a Race
          ///// &lt;/summary&gt;
          ///// &lt;remarks&gt;
          ///// Gets the all the results from the race ID
          ///// &lt;/remarks&gt;
          ///// &lt;param name="id"&gt;Race ID&lt;/param&gt;
          ///// &lt;returns&gt;All results from the given Race ID&lt;/returns&gt;
          //[HttpGet("{id}/results", Name = "GetResultsByRaceID")]
          //public IEnumerable&lt;Results&gt; GetAllResults(long id)
          //{
          //    List&lt;Results&gt; raceresults = new List&lt;Results&gt;();
          //    var raceid = id;
  
          //    foreach (var raceentry in _context.RaceEntries.Where(p =&gt; p.OwnerId == User.GetUserId())
          //                                                    .Where(p =&gt; p.RaceID == id))
          //    {
          //        var raceresult = new Results();
          //        var racedetails = _context.Races.Where(t =&gt; t.OwnerId == User.GetUserId())
          //                                            .FirstOrDefault(t =&gt; t.Id == raceid);
          //        var timingdetails = _context.Timings.Where(t =&gt; t.OwnerId == User.GetUserId())
          //                                                .FirstOrDefault(t =&gt; t.BibNumber == raceentry.BibNumber);
  
          //        var race = _context.Races.Where(t =&gt; t.OwnerId == User.GetUserId())
          //                             .FirstOrDefault(t =&gt; t.Id == id);
  
  
          //        raceresult.AthleteUserID = raceentry.AthleteUserId;
          //        raceresult.Category = "Category";
          //        raceresult.CategoryPosition = 1;
          //        raceresult.ChipTime = DateTime.Now; //timingdetails.EndTime - timingdetails.StartTime;
          //        raceresult.Club = raceentry.Club;
          //        raceresult.ClubPosition = 1;
          //        raceresult.EntryId = raceentry.Id;
          //        raceresult.FirstName = raceentry.FirstName;
          //        raceresult.Gender = raceentry.Gender;
          //        raceresult.GenderPosition = 1;
          //        raceresult.GunTime = DateTime.Now; //race.RaceStartTime - timingdetails.EndTime;
          //        raceresult.LastName = raceentry.LastName;
          //        raceresult.OverallPosition = 0;
          //        raceresult.RaceDate = race.RaceDate;
          //        raceresult.RaceID = raceid;
          //        raceresult.RaceName = race.Name;
          //        raceresult.ResultId = 1;
          //        raceresult.Team = raceentry.Team;
          //        raceresult.TeamPosition = 1;
          //        raceresults.Add(raceresult);
          //        //build result object
          //    }
          //    //Only return the data when it is owned by the same Id
          //    //Get only records where the OwnerId is not the logged in User.
          //    return raceresults.ToList();
          //}
  
          ///// &lt;summary&gt;
          ///// Publish the results of a Race
          ///// &lt;/summary&gt;
          ///// &lt;remarks&gt;
          ///// Publishes the results as Provisional or Final. Final will submit them to RunBritain/PO10
          ///// &lt;/remarks&gt;
          ///// &lt;returns&gt;The JSON for the created Race&lt;/returns&gt;
          //[HttpPost("{id}/publish", Name = "PublishResults")]
          //public IActionResult Publish([FromBody] Race item)
          //{
          //    if (item == null)
          //    {
          //        return BadRequest();
          //    }
  
          //    _context.Races.Add(item);
  
          //    //Set Owner ID
          //    item.OwnerId = User.GetUserId();
  
          //    _context.SaveChanges();
  
          //    return CreatedAtRoute("GetRace", new { id = item.Id }, item);
          //}
  
          /// &lt;summary&gt;
          /// Creates a Race
          /// &lt;/summary&gt;
          /// &lt;remarks&gt;
          /// Creates a Race which can have entrants and timings assigned to it.
          /// &lt;/remarks&gt;
          [HttpPost]
          public IActionResult Create([FromBody] RacePost item)
          {
              if (item == null)
              {
                  return BadRequest();
              }
  
              if (item.Name == null)
              {
                  return BadRequest("The Race must have a Name");
              }
  
              var raceitem = new Race
              {
                  CurrentEntries = item.CurrentEntries,
                  Description = item.Description,
                  MaxEntries = item.MaxEntries,
                  Name = item.Name,
                  ContactName = item.ContactName,
                  ContactEmail = item.ContactEmail,
                  ContactNumber = item.ContactNumber,
                  OwnerID = _userManager.GetUserId(User),
                  RaceDate = item.RaceDate,
                  RaceStartTime = item.RaceStartTime,
                  IsCompleted = item.IsCompleted,
                  IsPublished = item.IsPublished,
                  IsOpenForEntries = item.IsOpenForEntries,
                  LastUpdated = DateTime.Now
              };
  
              _context.Races.Add(raceitem);
  
              _context.SaveChanges();
  
              return CreatedAtRoute("GetRace", new { id = raceitem.ID }, raceitem);
          }
  
          /// &lt;summary&gt;
          /// Update a Race
          /// &lt;/summary&gt;
          /// &lt;remarks&gt;
          /// Update's a Race's details
          /// &lt;/remarks&gt;
          /// &lt;param name="id"&gt;Race ID&lt;/param&gt;
          /// &lt;returns&gt;The JSON for the updated Race&lt;/returns&gt;
          [HttpPut("{id}")]
          public IActionResult Update(long id, [FromBody] Race item)
          {
              if (item == null)
              {
                  return BadRequest();
              }
  
              if (item.Name == null)
              {
                  return BadRequest("The Race must have a Name");
              }
  
              var race = _context.Races.Where(t =&gt; t.OwnerID == _userManager.GetUserId(User))
                                       .FirstOrDefault(t =&gt; t.ID == id);
              //var race = _context.Races.FirstOrDefault(t =&gt; t.ID == id);
              if (race == null)
              {
                  return NotFound();
              }
  
              race.OwnerID = _userManager.GetUserId(User);
              race.Name = item.Name;
              race.ContactName = item.ContactName;
              race.ContactEmail = item.ContactEmail;
              race.ContactNumber = item.ContactNumber;
              race.RaceDate = item.RaceDate;
              race.RaceStartTime = item.RaceStartTime;
              race.Description = item.Description;
              race.MaxEntries = item.MaxEntries;
              race.CurrentEntries = item.CurrentEntries;
              race.IsCompleted = item.IsCompleted;
              race.IsPublished = item.IsPublished;
              race.IsOpenForEntries = item.IsOpenForEntries;
              race.LastUpdated = DateTime.Now;
  
  
              _context.Races.Update(race);
              _context.SaveChanges();
              return new NoContentResult();
          }
  
  
          /// &lt;summary&gt;
          /// Delete a Race
          /// &lt;/summary&gt;
          /// &lt;remarks&gt;
          /// Deletes a Race. Note: This will orphan any related result data and is not recommended!
          /// &lt;/remarks&gt;
          /// &lt;param name="id"&gt;Race ID&lt;/param&gt;
          /// &lt;returns&gt;&lt;/returns&gt;
          [HttpDelete("{id}")]
          public IActionResult Delete(long id)
          {
              var race = _context.Races.Where(p =&gt; p.OwnerID == _userManager.GetUserId(User)).FirstOrDefault(t =&gt; t.ID == id);
              //var race = _context.Races.FirstOrDefault(t =&gt; t.Id == id);
  
              if (race == null)
              {
                  return NotFound();
              }
  
              var raceid = race.ID;
  
              ////Delete associated race entries
              //foreach (var raceentry in _context.RaceEntries.Where(p =&gt; p.OwnerId == User.GetUserId())
              //                                                .Where(p =&gt; p.RaceID == raceid))
              //{
              //    _context.RaceEntries.Remove(raceentry);
              //}
  
              ////Delete associated race timings
              //foreach (var timing in _context.Timings.Where(p =&gt; p.OwnerId == User.GetUserId())
              //                                                .Where(p =&gt; p.RaceId == raceid))
              //{
              //    _context.Timings.Remove(timing);
              //}
  
              //Delete/Save the deletion of the race
              _context.SaveChanges();
              return new NoContentResult();
          }
      }
  }
  </code></pre>