401 Unauthorized : WWW-Authenticate: Bearer
Asked Answered
W

5

5

I've seen similar threads to this issue, but I had no luck solving it. LogIn worked successfuly but when I try to GET data of the user loggedIn in my home page I receive this error 401 Unauthorized the same erroe is showen also in Postman

My startup.cs

 public class Startup
    {
        private string _connectionString=null;
        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)
        {
            //Inject AppSettings
            services.Configure<ApplicationSettings>(Configuration.GetSection("ApplicationSettings"));
            _connectionString = Configuration["secretConnectionstring"];

            //without this it will define for example id to Id 
            services.AddMvc()
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
                .AddJsonOptions(options => {
                    var resolver = options.SerializerSettings.ContractResolver;
                    if (resolver != null)
                        (resolver as DefaultContractResolver).NamingStrategy = null;
                });

            services.AddEntityFrameworkNpgsql()
                .AddDbContext<ApiContext>(
                opt => opt.UseNpgsql(_connectionString));

            services.AddEntityFrameworkNpgsql()
                .AddDbContext<AuthentificationContext>(
                options => options.UseNpgsql(_connectionString));
            services.AddDefaultIdentity<ApplicationUser>()
                .AddEntityFrameworkStores<AuthentificationContext>();

            services.Configure<IdentityOptions>(options => {
                options.Password.RequireDigit = false;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireLowercase = false;
                options.Password.RequireUppercase = false;
                options.Password.RequiredLength = 4;
                });

            services.AddCors(options =>
            {
                options.AddPolicy("CorsPolicy",
                    builder => builder.AllowAnyOrigin()
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                   .AllowCredentials());
            });

            // Jwt Authentification
           var key = Encoding.UTF8.GetBytes(Configuration["ApplicationSettings:JWT_Secret"].ToString());

            services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(x=> {
                x.RequireHttpsMetadata = false;
                x.SaveToken = false;
                x.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ValidateIssuer = false,
                    ValidateAudience = false,
                    ClockSkew = TimeSpan.Zero
                };
            });

            services.AddTransient<dataSeed>();
            // In production, the Angular files will be served from this directory
            services.AddSpaStaticFiles(configuration =>
            {
                configuration.RootPath = "ClientApp/dist";
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, dataSeed seed)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseSpaStaticFiles();

            // global policy - assign here or on each controller
            app.UseCors("CorsPolicy");


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

            app.UseSpa(spa =>
            {
                // To learn more about options for serving an Angular SPA from ASP.NET Core,
                // see https://go.microsoft.com/fwlink/?linkid=864501

                spa.Options.SourcePath = "ClientApp";

                if (env.IsDevelopment())
                {
                    spa.UseAngularCliServer(npmScript: "start");
                }
            });

            app.UseAuthentication();



        }
    }
}

UserprofileController

{
    [Route("api/[controller]")]
    [ApiController]
    public class UserProfileController : ControllerBase
    {
        private UserManager<ApplicationUser> _userManager;
        public UserProfileController(UserManager<ApplicationUser> userManager)
        {
            _userManager = userManager;
        }

        [HttpGet]
        [Authorize]
        //GET : /api/UserProfile
        public async Task<Object> GetUserProfile()
        {
            string userId = User.Claims.First(c => c.Type == "UserID").Value;
            var user = await _userManager.FindByIdAsync(userId);
            return new
            {
                user.fullName,
                user.Email,
                user.UserName
            };
        }
    }
}

UserServices

 headers = {
    headers: new HttpHeaders({
      'Content-Type': 'application/json',
      'Accept': 'application/json'
    })
  }
  readonly BaseUrl = 'http://localhost:53847/api';

  constructor(private fb: FormBuilder, private http: HttpClient) { }

  formModel = this.fb.group({
    UserName: ['', Validators.required],
    Email: ['', Validators.email],
    fullName: ['', Validators.required],
    Passwords: this.fb.group({
      Password: ['',[Validators.required, Validators.minLength(4)]],
      ConfirmPassword: ['', Validators.required],
    }, { validator : this.comparePasswords})


  });
  comparePasswords(fb: FormGroup) {

    let confirmPswdCtrl = fb.get('ConfirmPassword');
    //passowrdMismatch
    //confirmPswdCtrl.errors={passowrdMismatch:true}
    if (confirmPswdCtrl.errors == null || 'passowrdMismatch' in confirmPswdCtrl.errors) {
      if (fb.get('Password').value != confirmPswdCtrl.value)
        confirmPswdCtrl.setErrors({ passowrdMismatch: true });
      else
        confirmPswdCtrl.setErrors(null);

    }

  }

  register() {
    var body = {
      UserName: this.formModel.value.UserName,
      Email: this.formModel.value.Email,
      fullName: this.formModel.value.fullName,
      Password: this.formModel.value.Passwords.Password,

    };
    return this.http.post(this.BaseUrl + '/ApplicationUser/Register', body, this.headers);
  }

  login(formData) {
    return this.http.post(this.BaseUrl + '/ApplicationUser/Login', formData, this.headers);

  }
  getUserProfile() {
    var tokenHeader = new HttpHeaders({ 'Authorization': 'Bearer' + localStorage.getItem('token'), 'Content-Type': 'application/json' });
    return this.http.get(this.BaseUrl + '/UserProfile', { headers: tokenHeader });
  }
}

ApplicationUserController the PostMethod

  [HttpPost]
        [Route("Login")]
        //POST : /api/ApplicationUser/Login
        public async Task<IActionResult> Login(LoginModel model)
        {
            var user = await _userManager.FindByNameAsync(model.UserName);
            if (user != null && await _userManager.CheckPasswordAsync(user, model.Password))
            {
                var tokenDescriptor = new SecurityTokenDescriptor
                {
                    Subject = new ClaimsIdentity(new Claim[]
                    {
                        new Claim("UserID",user.Id.ToString())
                    }),
                    Expires = DateTime.UtcNow.AddDays(1),
                    SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_appSettings.JWT_Secret)), SecurityAlgorithms.HmacSha256Signature)
                };
                var tokenHandler = new JwtSecurityTokenHandler();
                var securityToken = tokenHandler.CreateToken(tokenDescriptor);
                var token = tokenHandler.WriteToken(securityToken);
                return Ok(new { token });
            }
            else
                return BadRequest(new { message = "Username or password is incorrect." });
        }
    }

Help Plz .. Thx

Waterer answered 21/4, 2019 at 8:7 Comment(3)
I see your headers missing Bearer Token!Burnisher
@Burnisher in my startup.cs ??? I've allowed anyHeaders and In Postman it shows the token in the headers and in my getUserProfile getUserProfile() { var tokenHeader = new HttpHeaders({ 'Authorization': 'Bearer' + localStorage.getItem('token'), 'Content-Type': 'application/json' }); return this.http.get(this.BaseUrl + '/UserProfile', { headers: tokenHeader }); }Waterer
do you actually use asp.net mvc or asp.net core mvc?Pictograph
W
32

In my case I wasn't getting an error and everything appeared to work but my API returned 401 every time. Having banged my head a lot on this. I had ...

[Authorize]

on my Controller and found that the site was trying to use cookie authentication so although my JWT worked fine, the lack of a cookie auth made it fail. I changed the attribute to ...

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

and this fixed the issue as now the controller ignores cookie auth and concentrates only on jwt. Hope this helps someone

Waterer answered 2/5, 2019 at 11:14 Comment(3)
Super like. This is the problem I have faced and you gave me the right path. Kudos.Tannatannage
To avoid setting AuthenticationSchemes every time add this in startup. services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; }) not services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme), the latter didn't work for me.Evanevander
For me, adding app.UseAuthentication() did the trick.Heft
H
9

I found that changing the order of statements was my problem. Configure() requires ASP.NET Core middleware to be in the correct order.

This DID NOT work, and required me to add [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] to every controller...

app.UseAuthorization();
app.UseAuthentication();

This DOES work:

app.UseAuthentication();
app.UseAuthorization();
Halsy answered 1/8, 2020 at 19:54 Comment(3)
Thank. It work for me.Selfoperating
Thanks. it works only changing the order "app.UseAuthentication(); app.UseAuthorization();"Hobie
Worked for me app.UseAuthentication(); app.UseAuthorization();Transnational
G
3

One problem I see is here:

var tokenHeader = new HttpHeaders({ 'Authorization': 'Bearer' + localStorage.getItem('token'), 'Content-Type': 'application/json' });

When specifying a Bearer token, you need to leave a space between Bearer and the token itself, so that the result looks like this:

Authorization: <type> <credentials>

In your case, that would translate to:

Authorization: Bearer token

However, if you look at the code above, you'll see you're actually going to supply it like so:

Authorization: Bearertoken

which isn't going to work. Therefore, change your code to be:

var tokenHeader = new HttpHeaders({ 'Authorization': 'Bearer ' + localStorage.getItem('token'), 'Content-Type': 'application/json' });
// ---------------------------------------------------------^ Notice I've added a space here.
Glyconeogenesis answered 21/4, 2019 at 8:37 Comment(2)
@SouhaKhemiri Have you checked that token has actually a value or not?Burnisher
yes it has i can see it throw the postman or the inspector in section application I can see my tokenWaterer
P
0

The code you show does not have the UseAuthorization() declaration.

In .NET 6 I am doing:

builder.Services.AddAuthentication(opt =>
        {
            opt.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme;
            opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer("Bearer", options =>
        {
            options.Authority = "https://localhost:XXXX";
            options.Audience = "idwebclient";
            options.TokenValidationParameters.ValidateAudience = false;
            options.TokenValidationParameters.IssuerSigningKey =
                new SymmetricSecurityKey(Encoding.UTF8.GetBytes("XXXXXXXXXXXXXXXXXXXXXXXXX"));
        });
Patricio answered 13/6, 2022 at 22:18 Comment(0)
A
0

Recap

In my case I was not using any Identity Server Yet I was providing the Host as a ValidIssuer. It validated the Authority for the algo and keys which returned nothing, this caused the system to throw an unhandled exception. Solved this By Removing options.Authority from JwtBearerOptions in AddJwtBearer(options => ...).

After that I faced the 401 ERROR, resolved it by removing options.Audience from JwtBearerOptions in AddJwtBearer(options => ...), Also added ValidateLifetime to TokenValidationParameters (which you can see below in part 1)


Code

PART (1) JWT Configuration

in .NET 6 :

builder.services.AddAuthentication(options => 
{
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options => 
 {
    options.RequireHttpsMetadata = false;
    options.SaveToken = true;
    options.TokenValidationParameters = new TokenValidationParameters() 
    {
       ValidateIssuerSigningKey = jwtSettings.ValidateIssuerSigningKey,
       IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.IssuerSigningKey)),
       ValidateIssuer = jwtSettings.ValidateIssuer,
       ValidIssuer = jwtSettings.ValidIssuer,
       ValidateAudience = jwtSettings.ValidateAudience,
       ValidAudience = jwtSettings.ValidAudience,
       RequireExpirationTime = jwtSettings.RequireExpirationTime,
       ValidateLifetime = jwtSettings.RequireExpirationTime,
       ClockSkew = TimeSpan.FromDays(1),
    };
});

Extra

GET your JWT Settings from Appsettings using Either this Where

"JsonWebTokenKeys"

is the name of section in configuration :

var jwtSettings = new JwtSettings();
Configuration.Bind("JsonWebTokenKeys", jwtSettings);
builder.services.AddSingleton(jwtSettings);

//PART (1) => JWT Configuration goes here
//..
//.. 

OR this :

services.Configure<JwtSettings>(configuration.GetSection("JsonWebTokenKeys"));
using (ServiceProvider serviceProvider = services.BuildServiceProvider())
{
   var jwtSettings = serviceProvider.GetRequiredService<IOptions<JwtSettings>>().Value;
   
   //PART (1) => JWT Configuration goes here
   //..
   //.. 
}
           
Astrophysics answered 25/7, 2022 at 15:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.