Hello there,
I'm trying to do a JWT authentication in my web api application. But it always returns HTTP 401 unauthorized when i try access a route marked with [Authorize("Bearer")]
Follows the code:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DaoContext>(options =>
{
options.UseMySql(Configuration.GetConnectionString("DefaultConnection"));
});
services.AddCors(config =>
{
var policy = new CorsPolicy();
policy.Headers.Add("*");
policy.Methods.Add("*");
policy.Origins.Add("*");
policy.SupportsCredentials = true;
config.AddPolicy("policy", policy);
});
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build());
});
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
IssuerSigningKey = new RsaSecurityKey(new RSACryptoServiceProvider(2048).ExportParameters(true)),
ValidAudience = "Audience",
ValidIssuer = "Issuer",
ValidateIssuerSigningKey = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(0)
};
});
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseExceptionHandler(appBuilder =>
{
appBuilder.Use(async (context, next) =>
{
var error = context.Features[typeof(IExceptionHandlerFeature)] as IExceptionHandlerFeature;
//when authorization has failed, should retrun a json message to client
if (error != null && error.Error is SecurityTokenExpiredException)
{
context.Response.StatusCode = 401;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(new
{
State = "Unauthorized",
Msg = "token expired"
}));
}
//when orther error, retrun a error message json to client
else if (error != null && error.Error != null)
{
context.Response.StatusCode = 500;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(new
{
State = "Internal Server Error",
Msg = error.Error.Message
}));
}
//when no error, do next.
else await next();
});
});
app.UseCors("policy");
app.UseAuthentication();
app.UseMvc();
}
TokenAuthController.cs
[Route("api/[controller]")]
public class TokenAuthController : Controller
{
private UserRepository userRepository;
public TokenAuthController()
{
this.userRepository = new UserRepository();
}
[HttpPost]
public string GetAuthToken([FromBody]User user)
{
var existUser = userRepository.Login(user.Email, user.Password); //new User(); //UserStorage.Users.FirstOrDefault(u => u.Username == user.Username && u.Password == user.Password);
if (existUser != null)
{
var requestAt = DateTime.Now;
var expiresIn = requestAt.Add(TimeSpan.FromMinutes(30));
var token = GenerateToken(existUser, expiresIn);
return JsonConvert.SerializeObject(new
{
requestAt = requestAt,
expiresIn = TimeSpan.FromMinutes(30).TotalSeconds,
tokeyType = "Bearer",
accessToken = token
});
}
else
{
return JsonConvert.SerializeObject(new
{
Msg = "Username or password is invalid"
});
}
}
private string GenerateToken(User user, DateTime expires)
{
var handler = new JwtSecurityTokenHandler();
ClaimsIdentity identity = new ClaimsIdentity(
new GenericIdentity(user.Email, "TokenAuth"),
new[] {
new Claim("ID", user.ID.ToString())
}
);
var securityToken = handler.CreateToken(new SecurityTokenDescriptor
{
Issuer = "Issuer",
Audience = "Audience",
SigningCredentials = new SigningCredentials(new RsaSecurityKey(new RSACryptoServiceProvider(2048).ExportParameters(true)), SecurityAlgorithms.RsaSha256Signature),
Subject = identity,
Expires = expires,
NotBefore = DateTime.Now.Subtract(TimeSpan.FromMinutes(30))
});
return handler.WriteToken(securityToken);
}
[HttpGet]
[Authorize("Bearer")]
public string GetUserInfo()
{
var claimsIdentity = User.Identity as ClaimsIdentity;
return JsonConvert.SerializeObject(new
{
UserName = claimsIdentity.Name
});
}
}
ValuesController.cs
[Authorize("Bearer")]
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
public IEnumerable<User> Get()
{
UserRepository repository = new UserRepository();
return repository.SelectAll();
}
}
HTTP Request:
The generation of token is working, as show the image below:
The token is passed as Header("Authorization", "Bearer " + token); but the code returns 401.
In output, it throws a exception when i try GET /api/values
Exception thrown: 'Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException' in System.IdentityModel.Tokens.Jwt.dll
Is there a message for that exception?
No, there is not any message. That exception is only displayed into output.
What is creating the token? Can you cut and paste an actual token so we can decode it? It's hard to transcribe from a screenshot.
Here's a token for you:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6InNpbWFzLmx1Y2FzQGhvdG1haWwuY29tIiwiSUQiOiI0IiwibmJmIjoxNTA1NTM1NDg4LCJleHAiOjE1MDU1MzcyODgsImlhdCI6MTUwNTUzNTQ4OCwiaXNzIjoiSXNzdWVyIiwiYXVkIjoiQXVkaWVuY2UifQ.tSabh2Qx_6tBDEM1aG1Gx3wQgUMvG59fxk6jBYhzs-kDMhzifFNPzQeYcpkoLz5aAHfUmb2Gf0Oo0uk8xtvmnlJzTm0hXrMInRZUc-clHlhsOERVoydaVLTtnUlhiv0FMy6sOVG3olKncsup--7N-zvVBBAKVZ3hp4mMCCNjoKnEcTdC_xMYa8VLxLwh7hKkzED7a1T5flY5vXpfugx4EvIVU2SW_I_6TdE22wmNUmX-q8A_C0dE0an6ZZGWKRB46h3H_z0E76OLCoLZXWemTW6OWvzUOxU6e62nok9CRsXkfpYCxARyqJO5uG-r_BlnyRKULPMEWwTaVp-7QXI8hA
Decoding from http://calebb.net/
{
alg: "RS256",
typ: "JWT"
}.
{
unique_name: "simas.[email protected]",
ID: "4",
nbf: 1505535488,
exp: 1505537288,
iat: 1505535488,
iss: "Issuer",
aud: "Audience"
}.
[signature]
The source code can be found here, if you want
https://github.com/lucassklp/DefaultArchitecture
@lucassklp the solution is here:
The response is 200 OK.
In your code ,the problem is "The signature is invalid"
So I think when it generates SecurityKey and then verify it ,it has changed.
Thanks @zzyykk123456 for replying. Worked here, I commited the new version. Do you know why it was wrong on the old code?
I have this same problem with .net core 2.0 and JwtBearer tokens.
[HttpGet]
[Route("api/v1/tokens")]
[Authorize] // Throws WWW-Authenticate: Bearer error="invalid_token", error_description="The signature is invalid"
Example of this issue can be reproduced from this GitHub repo:
https://github.com/justintubbs/NetCore2_JWTExample
Thanks, @zzyykk123456 that fixed it for me, too!
Please help me to in case i use with angular5
https://github.com/uopeydel/AngAuthCoreLearn
It alway said 401 ,I make sure i already append header to backend and in db a token same token in frontend
*API have guard
```
[Route("api/[controller]")]
// [Authorize]
[EnableCors("AllowAll")]
//IF comment this below [Authorize("WebBoardAuthorize")] will be surely see a token HttpContext.Request.Headers
//[Authorize("WebBoardAuthorize")]
public class ApiTestController : Controller
{
[HttpPost("www")]
public IActionResult Post([FromBody]DumModel value)
{
var context = HttpContext.Request.Headers;
return Ok($"xxxx www { value } ");
}
public class DumModel {
public string Value { get; set; }
}
}
*frontend post
```
LearPost() {
this.ppPost().subscribe(
Response => {
console.log("post success")
console.log(Response);
},
err => {
//unauthorized refresh token
console.log("unauth")
console.log("err", err)
this.errTxt = err;
//if (err.status == 401) {
// //re function
// this.LearPost();
//}
}
);
}
ppPost(): Observable<string> {
let url = this._config.apiWebBoardURL + "api/ApiTest/www";
let headers = new HttpHeaders({ 'Content-Type': 'application/json' });
let thsSub = this.http.post<string>(url, { value: "s1" }, { headers })
return thsSub;
}
*generate token
public async Task<object> PublishToken(Models.AccessTokenDto detail)
{
var now = detail.now;
// Specifically add the jti (nonce), iat (issued timestamp), and sub (subject/user) claims.
// You can add other claims here, if you want:
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, detail.Sub),
new Claim(JwtRegisteredClaimNames.Jti, detail.Jti),
new Claim(JwtRegisteredClaimNames.Iat, detail.Iat , ClaimValueTypes.Integer64),
new Claim("roles",detail.Roles)
};
// Create the JWT and write it to a string
var jwt = new JwtSecurityToken(
issuer: detail.Issuer,
audience: detail.Audience,
claims: claims,
notBefore: now,
expires: now.Add(Expiration()),
signingCredentials: detail.hashSecret);
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
var response = new
{
access_token = encodedJwt,
expires_in = (int)Expiration().TotalSeconds,
refresh_token = Guid.NewGuid().ToString()
};
// Keep a access_token to tokensRepository (REDIS)
int time_exp_in_redis = 14400; //unit of time is seconds
var TokensForLogin = new TokensForLoginDto();
TokensForLogin.Secret = detail.Secret;
TokensForLogin.Issuer = detail.Issuer;
TokensForLogin.Audience = detail.Audience;
var str_secret = Convert.ToBase64String(detail.Secret);
//mock detail.Roles = "admin+saleco+salemanager";
await TokenRepository.SetValue(response.access_token, str_secret, time_exp_in_redis); //await token_repo.SetValue(response.access_token, JsonConvert.SerializeObject(TokensForLogin, Formatting.None), time_exp_in_redis);
// Keep a refresh_token to tokensRepository (REDIS)
var TokensForRefresh = new TokensForRefreshDto();
TokensForRefresh.access_token = response.access_token;
TokensForRefresh.identity = "IDENTITY";
await TokenRepository.SetValue(response.refresh_token, response.access_token, time_exp_in_redis); //await token_repo.SetValue(response.refresh_token, JsonConvert.SerializeObject(TokensForRefresh, Formatting.None), time_exp_in_redis);
return response;
}
Config service in startup
```
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext
(options => options
.UseSqlServer("Data Source=DESKTOP-KR0EJIO;Initial Catalog=WebBoardAuth;Persist Security Info=True;User ID=sa;Password=123456789"));
#region Resault server
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
// Add Token SecurityKey
_symmetricSecurityKey = new SymmetricSecurityKey(Convert.FromBase64String(secret_key));
//_symmetricSecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes( secret_key));
tokenOptions = new TokenAuthOptions()
{
Audience = client_id,
Issuer = "iAmWebBoardNetCore",
SigningCredentials = new SigningCredentials(_symmetricSecurityKey, "HS256"),
Secret_key = secret_key
};
services.AddSingleton<TokenAuthOptions>(tokenOptions);
services.AddAuthorization(auth =>
{
auth.AddPolicy("WebBoardAuthorize", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build());
});
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Audience = Configuration.GetSection("TokenProviderOptions:Audience").Value;
options.ClaimsIssuer = Configuration.GetSection("TokenProviderOptions:Issuer").Value;
options.TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = _symmetricSecurityKey,
ValidAudience = tokenOptions.Audience,
ValidIssuer = tokenOptions.Issuer,
ValidateLifetime = true
};
options.SaveToken = true;
});
#endregion
#region Auth Server
services.AddIdentity<ApplicationUser, IdentityRole>(
options =>
{
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequiredLength = 6;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(10);
options.Lockout.MaxFailedAccessAttempts = 5;
// INITIALIZE TOKEN PROVIDER DESCRIPTOR FOR PRE-RESET PASSWORD'S TIME SPAN
options.Tokens.ProviderMap.Add("Default", new TokenProviderDescriptor(typeof(ITokenProviderDescriptor<ApplicationUser>)));
})
.AddEntityFrameworkStores<AuthContext>()
.AddDefaultTokenProviders();
//services.AddMvc();
services.AddMvc(options =>
{
options.InputFormatters.Insert(0, new JilInputFormatter());
options.OutputFormatters.Insert(0, new JilOutputFormatter());
options.Conventions.Add(new NameSpaceVersionRoutingConvention("api"));
});
//add swagger interactive documentation
services.AddSwaggerGen(config =>
{
config.SwaggerDoc("v1", new Info { Title = "WebBoard Auth API", Version = "v1" });
});
//add permission enable cross-origin requests (CORS) from angular
var corsBuilder = new CorsPolicyBuilder();
corsBuilder.AllowAnyHeader();
corsBuilder.AllowAnyMethod();
corsBuilder.AllowAnyOrigin();
corsBuilder.AllowCredentials();
services.AddCors(options =>
{
options.AddPolicy("AllowAll", corsBuilder.Build());
});
//services.AddSingleton<IRedisConnectionMultiplexer, RedisConnectionMultiplexer>();
services.AddScoped<AuthDbConnection>();
#endregion
services.AddScoped<IEntityFrameworkContext, EntityFrameworkContext>();
services.AddScoped<IEntityUnitOfWork, EntityUnitOfWork>();
services.AddScoped<Logic.UnitOfWork.Interface.ILogicUnitOfWork, Logic.UnitOfWork.Implement.LogicUnitOfWork>();
services.AddScoped<WebBoardAuth.Logic.ILogicUnitOfWork, WebBoardAuth.Logic.LogicUnitOfWork>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
```
@uopeydel Did you find a solution?
Hello there,
I'm trying to do a JWT authentication in my web API application. But it always returns HTTP 401 unauthorized when I try to access a route marked with [Authorize("Bearer")]
Startup.cs
Follows the code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.IO;
using Microsoft.Extensions.Configuration;
using ERS_CAT.DataAccess;
using ERS_CAT.DataAccess.Abstract;
using ERS_CAT.DataAccess.Helpers;
using ERS_CAT.Utility.Common.Abstract;
using ERS_CAT.Utility.Common;
using ERS_CAT.Business.Abstract;
using ERS_CAT.Business;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using ERS_CAT.API.Auth;
using Microsoft.AspNetCore.Mvc.Cors.Internal;
using System.Text;
using System.Security.Cryptography;
namespace ERS_CAT.API
{
public class Startup
{
private const string DefaultCorsPolicyName = "localhost";
//private readonly IConfigurationRoot _appConfiguration;
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);
if (env.IsEnvironment("Development"))
{
// This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
builder.AddApplicationInsightsSettings(developerMode: true);
}
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; set; }
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
// MVC
services.AddMvc(options =>
{
options.Filters.Add(new CorsAuthorizationFilterFactory(DefaultCorsPolicyName));
});
//Enable the use of an[Authorize("Bearer")] attribute on methods and classes to protect.
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build());
});
services.AddApplicationInsightsTelemetry(Configuration);
// Configure CORS for angular2 UI
var origins = Configuration["CorsOrigins"].Split(',').ToArray();
services.AddCors(options =>
{
options.AddPolicy(DefaultCorsPolicyName, builder =>
{
// App:CorsOrigins in appsettings.json can contain more than one address separated by comma.
builder
.WithOrigins(origins)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
.Build();
});
});
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
var keybytes = Encoding.ASCII.GetBytes("f9a32479-4549-4cf2-ba47-daa00cf2afc");
var signingKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(keybytes);
options.TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = signingKey,
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Issuer"]
};
});
// Add framework services.
services.AddMvc();
services.AddSingleton<IConfiguration>(Configuration);
// When ever we are going use IConfiguration there we are going to get instance of Configuration
//Dependancy injection
services.AddTransient<IERS_CATUoW, ERS_CATUoW>();
services.AddTransient<IAdminService, AdminService>();
services.AddTransient<IAnalyticsService, AnalyticsService>();
services.AddTransient<ICheckInCheckOutReportService, CheckInCheckOutReportService>();
services.AddTransient<ICheckInService, CheckInService>();
services.AddTransient<ICheckOutService, CheckOutService>();
services.AddTransient<IDashBoardService, DashBoardService>();
services.AddTransient<IFilterService, FilterService>();
services.AddTransient<IFinanceFilterService, FinanceFilterService>();
services.AddTransient<ILocationService, LocationService>();
services.AddTransient<IMapService, MapService>();
services.AddTransient<IProductService, ProductService>();
services.AddTransient<ISearchCheckInCheckOutService, SearchCheckInCheckOutService>();
services.AddSingleton<RepositoryFactories, RepositoryFactories>();
services.AddTransient<IRepositoryProvider, RepositoryProvider>();
services.AddTransient<IAdminService, AdminService>();
services.AddTransient<ICommon, ERS_CATCommon>();
var connectionString = Configuration.GetConnectionString("DefaultConnection");
ERS_CATDbContext.conString = connectionString;
}
// 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)
{
// global policy - assign here or on each controller
app.UseCors(DefaultCorsPolicyName);
//app.UseSession();
app.UseMvc();
app.Use(async (context, next) =>
{
await next();
if (context.Response.StatusCode == 404 &&
!Path.HasExtension(context.Request.Path.Value) &&
!context.Request.Path.Value.StartsWith("/api/"))
{
context.Request.Path = "/index.html";
await next();
}
});
app.UseMvcWithDefaultRoute();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseAuthentication();
app.UseJwtTokenMiddleware();
}
}
}
UserController .cs
using ERS_CAT.Business.Abstract;
using ERS_CAT.Entity.Models;
using ERS_CAT.Utility.Common;
using ERS_CAT.Utility.Common.Abstract;
using ERS_CAT.API.Auth;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using ERS_CAT.Web.Controllers;
using ERS_CAT.Utility.Security;
using Microsoft.Extensions.Configuration;
using System.Security.Principal;
using System.Text;
using System.Security.Cryptography;
namespace Gridco.Web.Controllers
{
[Route("api/[controller]")]
public class UserController : BaseController
{
private ICommon _commonService;
private IAdminService _adminservice;
IConfiguration _iconfiguration;
public object Encryptor { get; private set; }
public UserController(ICommon CommonService, IAdminService AdminService, IConfiguration iconfiguration) : base()
{
_commonService = CommonService;
_adminservice = AdminService;
_iconfiguration = iconfiguration;
}
[AllowAnonymous]
[HttpPost("GrantResourceOwnerCredentials")]
public JsonResult GrantResourceOwnerCredentials([FromBody]User user)
{
User providerResponse = _adminservice.CheckLogin(user);
if (providerResponse != null)
{
var requestAt = DateTime.Now;
var expiresIn = requestAt.Add(TimeSpan.FromMinutes(30));
var accessToken = GenerateToken(providerResponse, expiresIn);
//var accessToken = CreateToken(providerResponse, expiresIn);
var Result = new RequestResult();
Result.State = RequestState.Success;
Result.Data = new { expiresIn = TokenAuthOption.ExpiresSpan.TotalSeconds, tokeyType = TokenAuthOption.TokenType, AccessToken = accessToken, UserInfo= providerResponse };
Result.Msg = "Authorized User";
return Json(Result);
}
else
{
var Result = new RequestResult();
Result.State = RequestState.NotAuth;
Result.Data = null;
Result.Msg = "Faild";
return Json(Result);
}
}
private string GenerateToken(User user, DateTime expires)
{
var handler = new JwtSecurityTokenHandler();
ClaimsIdentity identity = new ClaimsIdentity(
new GenericIdentity(user.User_Email, "TokenAuth"),
new[] {
new Claim("User_Id", user.User_ID.ToString())
}
);
var keybytes = Encoding.ASCII.GetBytes("f9a32479-4549-4cf2-ba47-daa00cf2afc");
var signingKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(keybytes);
var securityToken = handler.CreateToken(new SecurityTokenDescriptor
{
Issuer = "Issuer",
Audience = "Audience",
SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256),
Subject = identity,
Expires = expires,
NotBefore = DateTime.Now.Subtract(TimeSpan.FromMinutes(30))
});
return handler.WriteToken(securityToken);
}
[HttpGet("GetUserInfo")]
[Authorize("Bearer")]
public string GetUserInfo()
{
var claimsIdentity = User.Identity as ClaimsIdentity;
return JsonConvert.SerializeObject(new
{
UserName = claimsIdentity.Name
});
}
}
}
and also i have send the token in request header also the screenshot given below
but i still getting error unauthorized and invalid token in response header.
could you please have a look at it @zzyykk123456 .
Order matters, try this:
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseAuthentication();
app.UseJwtTokenMiddleware();
app.UseMvcWithDefaultRoute();
hi @Tratcher,
thank you for your quick response but it is not working, I am still getting the same error
The audience is invalid
Needs to match the issuer. Otherwise turn off validation.
Hi all
why is my case not working
service settings:
services.AddAuthorization(auth =>
{
auth.AddPolicy(JwtBearerDefaults.AuthenticationScheme, new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build());
});
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = configuration["Jwt:Issuer"],
ValidAudiences = new[] { configuration["Jwt:Issuer"] },
IssuerSigningKeys = new List<SecurityKey> {
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:Key"]) )}
};
});
in the controller
private string BuildToken(Tuple<Microsoft.AspNetCore.Identity.SignInResult, ClaimsPrincipal> loginResult)
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var handler = new JwtSecurityTokenHandler();
var token = handler.CreateToken(new SecurityTokenDescriptor
{
Issuer = _configuration["Jwt:Issuer"],
Audience = _configuration["Jwt:Issuer"],
SigningCredentials = creds,
Subject = new ClaimsIdentity( loginResult.Item2.Identity),
Expires = new DateTime?(DateTime.Now.AddDays(30))
});
return handler.WriteToken(token);
}
then I have a middleware custom
```
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseCors("CorsPolicy");
app.UseAuthentication();
app.UseAccountInfoMiddleware();
app.UseMvc();
the UseAccountInfoMiddleware is needed to populate an accountinfo object for the backend
but in the middleware httpcontext.user does not have the claims.
the strange thing is if I do
private bool HasValidIdentity(HttpContext httpContext, out ClaimsIdentity identity)
{
bool result = false;
identity = null;
ClaimsPrincipal user = null;
try
{
if (httpContext.RequestServices.GetService(typeof(IConfiguration)) is IConfiguration configuration)
{
SecurityToken validatedToken = null;
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
var bearer = httpContext.Request.Headers.FirstOrDefault(x => x.Key == "Authorization"
&& x.Value.FirstOrDefault().ToLowerInvariant().StartsWith("bearer"));
if (!string.IsNullOrEmpty(bearer.Value.FirstOrDefault()))
{
TokenValidationParameters validationParameters =
new TokenValidationParameters
{
ValidIssuer = configuration["Jwt:Issuer"],
ValidAudiences = new[] { configuration["Jwt:Issuer"] },
IssuerSigningKeys = new List<SecurityKey> { new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:Key"])) }
};
string token = bearer.Value.FirstOrDefault().Substring(6).Trim();
user = handler.ValidateToken(token, validationParameters, out validatedToken);
}
result = (user != null
&& user.Identity != null
&& user.Identity.IsAuthenticated
&& user.Identity is ClaimsIdentity
&& (user.Identity as ClaimsIdentity).Claims != null);
identity = result ? user.Identity as ClaimsIdentity : null;
}
}
catch (Exception)
{
result = false;
user = null;
identity = null;
}
return result;
}
the identity is filled with the correct claims, is the default auth middleware doing anything different?
For your configuration UseAuthentication will run JwtBearer on every request, but it won't reject anonymous requests. When you run UseAccountInfoMiddleware it should see the user for authenticated requests, but it won't for anonymous requests. identity.IsAuthenticated is one way to check for this.
The other thing you could do is to put your augmentation logic into JwtBearerOptions.Events.OnTokenValidated: https://github.com/aspnet/Security/blob/db19d87bfa41fa5a11a318d12f8f1f1e0e27c3df/src/Microsoft.AspNetCore.Authentication.JwtBearer/Events/JwtBearerEvents.cs#L27
@Tratcher Thank you for the info and it is working if I add
Microsoft.AspNetCore.Authorization.Authorize(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme)
but for me that is not very transparant. I think that every middleware is populating or changing the httpcontext.services and passing that to the next middleware, correct?
so how can a middleware that is (and think that that is what it is doing) populating the identy and come before my custom middleware, not populate HttpContext.user. but it is populating the HttpContext.user with the correct claims after my custom middleware?
case 1 (no authorize header)
app.UseAuthentication(); => _reading the token ???_
app.UseAccountInfoMiddleware(); => no HttpContext.User with claims (only when self reading the token)
app.UseMvc(); => no HttpContext.User with claims
case 2 (authorize header for bearer)
app.UseAuthentication(); => _reading the token ???_
app.UseAccountInfoMiddleware(); => no HttpContext.User with claims (only when self reading the token)
app.UseMvc(); => HttpContext.User with claims ????
That's going to take more debugging. Can you share a complete project on github?
Also, please open a new issue, we don't track feedback on closed issues.
@justintubbs , hello sir, did you find the solution of your problem. i have the same issue. i found the solution, i have used cookie authentication as well as jwt. so after putting authrize header on action get error. so i included [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
instead of [Authorize]
thank you.
@justintubbs not realy, and if I have some time I'll try to create a new standalone project and open a new ticket.
for now we are migrating to angular 5 and core 2.0, what is taken all of my time (it's a private project) but I must say the same middleware is working with core 1 using mixed cookie and jwt token. so for now we will do a manual read of the token in the middleware.
the reason why we have this middleware is because we need to populate and IAccountInfo object that is used in the bussiness code, what is about 10 years old, started the project in webforms.
https://blog.markvincze.com/secure-an-asp-net-core-api-with-firebase/
I've used this blog.
It works on localhost , but when deployed fails.
firebase Bearer error="invalid_token", error_description="The token is expired"
Any solution?
Any news here guys ?
Another contributor to my example has a fix, you can pull working code here:
https://github.com/justintubbs/NetCore2_JWTExample
The PR that fixed the issue is here:
https://github.com/justintubbs/NetCore2_JWTExample/pull/1
To test the code above....
1) Use Fiddler/Postman/etc to make a POST http://localhost:53840/api/v1/tokens
You'll get a 200 OK response with a JWT token in the body of the response
2) Form a 2nd request like this:
GET http://localhost:53840/api/v1/tokens
Accept: application/json
Authorization: Bearer {JWT goes here}
3) You'll get a 200 OK response instead of
HTTP/1.1 401 Unauthorized
Server: Kestrel
WWW-Authenticate: Bearer error="invalid_token", error_description="The signature is invalid"
X-SourceFiles: =?UTF-8?B?QzpcR2l0UmVwb3NcUGxheWdyb3VuZFxOZXRDb3JlMl9KV1RFeGFtcGxlXFdlYkFwcGxpY2F0aW9uMVxhcGlcdjFcdG9rZW5z?=
X-Powered-By: ASP.NET
Date: Mon, 21 May 2018 14:29:53 GMT
Content-Length: 0
At the end I've used this to solve the issue -
https://www.facebook.com/set4u.biz/posts/1652298138222965
Very nice extension..
you need to check the passed headline.
Maybe it does not contain a token.
it was like that in my project
I am having same problem,
Problem happens when you use two claims,
` var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.UserId.ToString()),
**//new Claim(ClaimTypes.Name, user.Username),** <------ this cause
}),`
if you just use one claim that works fine in Core 2.1
At the end I've used this to solve the issue -
https://www.facebook.com/set4u.biz/posts/1652298138222965
Very nice extension..
yeah, that doesn't look sketchy at all
Thanks @JonET. I've flagged that comment as spam.
Order matters, try this:
app.UseDefaultFiles(); app.UseStaticFiles(); app.UseAuthentication(); app.UseJwtTokenMiddleware(); app.UseMvcWithDefaultRoute();
And useauthentication needs to be before usehttpsredirection
And useauthentication needs to be before usehttpsredirection
?? I'd expect useauthentication _after_ usehttpsredirection, you want your auth to happen over https.
Hi,
@Tratcher
I have the same issue, but I have another approach to generate a token.
`
```
public async Task
{
GoogleCredential credential;
using (var stream = new System.IO.FileStream(path,
System.IO.FileMode.Open, FileAccess.Read))
{
credential = GoogleCredential.FromStream(stream).CreateScoped(
new string[] {
"https://www.googleapis.com/auth/userinfo.email" }
);
}
ITokenAccess c = credential as ITokenAccess;
return await c.GetAccessTokenForRequestAsync();
}
public void ConfigureServices(IServiceCollection services)
{
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://securetoken.google.com/test-alex-project";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "https://securetoken.google.com/test-alex-project",
ValidateAudience = true,
ValidAudience = "test-alex-project",
ValidateLifetime = true
};
});
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseMvc();
}
````
Token seems to be OK. But whenever I call the action with [Authorize] attribute I get 401.
Could anyone help me?
Some things that can help troubleshoot:
Error description can help you a lot:
WWW-Authenticate: Bearer error="invalid_token", error_description="The signature is invalid"
In my case: signature issue
You might see:
2019-08-01 08:32:07.7718|7|INFO|Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler|Bearer was not authenticated. Failure message: IDX10511: Signature validation failed. Keys tried: '[PII is hidden]'.
kid: '[PII is hidden]'.
Exceptions caught:
'[PII is hidden]'.
token: '[PII is hidden]'.
IdentityModelEventSource.ShowPII = true;
Will result in more info, including the exception:
2019-08-01 08:57:29.3627|7|INFO|Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler|Bearer was not authenticated. Failure message: IDX10511: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.X509SecurityKey , KeyId: NTE2ME...
'.
kid: 'NTE2ME...'.
Exceptions caught:
'Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: An internal error occurred
at Internal.Cryptography.Helpers.OpenStorageProvider(CngProvider provider)
at System.Security.Cryptography.CngKey.Import(Byte[] keyBlob, String curveName, CngKeyBlobFormat format, CngProvider provider)
at System.Security.Cryptography.CngKey.Import(Byte[] keyBlob, CngKeyBlobFormat format)
at Internal.Cryptography.Pal.X509Pal.DecodePublicKey(Oid oid, Byte[] encodedKeyValue, Byte[] encodedParameters, ICertificatePal certificatePal)
at System.Security.Cryptography.X509Certificates.PublicKey.get_Key()
at Microsoft.IdentityModel.Tokens.X509SecurityKey.get_PublicKey()
at Microsoft.IdentityModel.Tokens.SupportedAlgorithms.IsSupportedAlgorithm(String algorithm, SecurityKey key)
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateSignatureProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(Byte[] encodedBytes, Byte[] signature, SecurityKey key, String algorithm, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)
'.
token: '{"typ":"JWT","alg":"RS256", ...
Solved it for me, because the app pool account didn't have permission to start this itself.
@justintubbs , hello sir, did you find the solution of your problem. i have the same issue. i found the solution, i have used cookie authentication as well as jwt. so after putting authrize header on action get error. so i included [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
This solved my issue.
Does anyone know why we need to use [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
instead of using just [Authorize]?
Is there any other way to configure this as default?
In oder to use [Authorize]
you can configure de default authorization like this:
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
});
Most helpful comment
@justintubbs , hello sir, did you find the solution of your problem. i have the same issue. i found the solution, i have used cookie authentication as well as jwt. so after putting authrize header on action get error. so i included [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
instead of [Authorize]
thank you.