I am trying to validate token in multi tenant application. In startup(single tenant) earlier used this code for getting configuration data from appsettings.json.
services.AddMicrosoftIdentityWebApiAuthentication(Configuration);
But now we need to load configurations data from database. So i have added below code to startup.
services.AddAuthentication("Bearer").AddJwtBearer("Bearer",
options =>
{
options.Authority = "http://localhost:3000";
options.Audience = "fcb78955-1a4a-6666-aa12-fc473b8fd8f6";
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new
TokenValidationParameters()
{
ValidateAudience = false
};
});
and this is my token validation code. I have hardcode this for testing purpose. so please ignore it.
public static async Task<ClaimsPrincipal> TokentValidate(string token, string tenantId, string clientId)
{
try
{
var authorityEndpoint = "https://demo.identityserver.io/";
authorityEndpoint = "https://login.microsoftonline.com/" + "9111f39b-e5ed-8899-9c13-0005388e683a" + "/";
var openIdConfigurationEndpoint = $"{authorityEndpoint}.well-known/openid-configuration";
IConfigurationManager<OpenIdConnectConfiguration> configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(openIdConfigurationEndpoint, new OpenIdConnectConfigurationRetriever());
OpenIdConnectConfiguration openIdConfig = await configurationManager.GetConfigurationAsync(CancellationToken.None);
clientId = "f0019d64-100a-4990-aa12-fc663b8899f6";
TokenValidationParameters validationParameters = new TokenValidationParameters
{
ValidIssuer = openIdConfig.Issuer,
ValidAudiences = new[] { clientId },
IssuerSigningKeys = openIdConfig.SigningKeys,
ValidateLifetime = true,
ValidateAudience = false,
ValidateIssuer = false,
ValidateIssuerSigningKey = true
};
SecurityToken validatedToken;
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
var user = handler.ValidateToken(token, validationParameters, out validatedToken);
return user;
}
catch (Exception ex)
{
return null;
}
}
But this method not calling. So I have removed startup code. then i am getting this error.
{"Error":"No authenticationScheme was specified, and there was no DefaultChallengeScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action configureOptions)."}
I want to if we are using multi tenant and loading configurations from database what is the correct way. If anyone have an idea please help.
Related
HttpContext Claims doesn't match with JWT.
I Tried Changed nameidentifier's value but context.user.claims value is always same.
The value changes only when I log in with a different account.
Get Claims Code:
public class HttpRequestInterceptor : DefaultHttpRequestInterceptor
{
public override ValueTask OnCreateAsync(
HttpContext context,
IRequestExecutor requestExecutor,
IQueryRequestBuilder requestBuilder,
CancellationToken cancellationToken)
{
string? userId =
context.User.FindFirstValue(ClaimTypes.NameIdentifier);
string? role =
context.User.FindFirstValue(ClaimTypes.Role);
string? companyId =
context.User.FindFirstValue(ClaimTypes.Actor);
if(!string.IsNullOrEmpty(userId) && !string.IsNullOrEmpty(role) && !string.IsNullOrEmpty(companyId))
{
requestBuilder.SetProperty("currentUser",
new CurrentUser(Guid.Parse(userId),Guid.Parse(companyId),role));
}
return base.OnCreateAsync(context,
requestExecutor,
requestBuilder,
cancellationToken);
}
}
Result:
Program.cs :
var jwtSetting = jwtSettingSection.Get<JWTSettings>();
var signingKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(jwtSetting.SecretKey!));
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtSetting.ValidIssuer,
ValidAudience = jwtSetting.ValidAudience,
IssuerSigningKey = signingKey,
};
service
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(opt =>
{
opt.TokenValidationParameters = tokenValidationParameters;
opt.MapInboundClaims = false;
});
service.AddAuthorization();
Please Help me
I changed the setting in the TokenValidationParameters (ValidateIssuer,ValidateAudience) from False to True.
What the settings used to be (startup) - everything was working perfectly.
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["TokenKey"])),
ValidateIssuer = false,
ValidateAudience = false,
};
});
Now every call to the API the requires to authorized stopped working.
The new settings - stopped working
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["TokenKey"])),
ValidateIssuer = true,
ValidateAudience = true,
};
});
Every call to the API that needs to be authorized now is getting unauthorized.
This is my token service:
public class TokenService : ITokenService
{
private readonly SymmetricSecurityKey _key;
private readonly UserManager<AppUser> _userManager;
public TokenService(IConfiguration config, UserManager<AppUser> userManager)
{
this._userManager = userManager;
_key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["TokenKey"]));
}
public async Task<string> CreateToken(AppUser user)
{
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.NameId, user.Id.ToString()),
new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName)
};
var roles = await _userManager.GetRolesAsync(user);
claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role)));
var creds = new SigningCredentials(_key, SecurityAlgorithms.HmacSha512Signature);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.Now.AddDays(7),
SigningCredentials = creds
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
}
Does anyone can please explain to me what causes it so I could learn. And how can I fix it ? I tried many ways but unfortunately, nothing seems to work for me.
Thank you so much.
This is the code at GitHub just in case : https://github.com/davidax0204/HeroMain.git
When you set ValidateIssuer and ValidateAudience to true, you should set value for ValidIssuer and ValidAudience when creating JWT and SecurityTokenDescriptor
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.Now.AddDays(7),
SigningCredentials = creds,
Audience = "YourAudience",
Issuer = "YourIssuer"
};
And adding same value to TokenValidationParameters properties
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["TokenKey"])),
ValidateIssuer = true,
ValidIssuer = "YourIssuer",
ValidateAudience = true,
ValidAudience = "YourAudience"
};
});
I'm trying to implement JWT authentication in a .NET 5 WebAPI. Everything works fine until I add more web servers to the application.
How could I store JWT, for example, in a SQL Server database instead of having them in memory on the single server?
Here's the implementation working on a single server:
Startup
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Context.JwtIssuer,
ValidAudience = Context.JwtIssuer,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Context.JwtSecretKey))
};
}
Controller
[HttpGet("Login")]
public IActionResult Login(string uid, string password)
{
var hash = _userManager.GetPasswordHash(uid);
if (string.IsNullOrEmpty(hash))
{
return NotFound("Account not found");
}
if (!_passwordHasher.Check(hash, password))
{
return Unauthorized("Uid/password not correct");
}
var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Context.JwtSecretKey));
var token = new JwtSecurityToken(
issuer: Context.JwtIssuer,
audience: Context.JwtIssuer,
expires: DateTime.Now.AddHours(3),
claims: new List<Claim>() { new Claim(ClaimTypes.Name, uid)},
signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)
);
return Ok(new
{
token = new JwtSecurityTokenHandler().WriteToken(token),
expiration = token.ValidTo
});
}
I'm working on .net core api 2.1, I have implemented JWT token authentication, I want jwt token to expire after given time, but it is not expiring. Token still validation even after expiry time.
Startup.cs code:
// configure jwt authentication
var jwtSettings = jwtSettingsSection.Get<JWTSettings>();
var key = Encoding.ASCII.GetBytes(jwtSettings.SECRET);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ClockSkew = TimeSpan.Zero
};
});
services.Configure<IISOptions>(options =>
{
options.AutomaticAuthentication = true;
//options.ForwardClientCertificate = true;
});
SignIn api code to create token on sign in:
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_jwtSettings.SECRET);
var currentTime = DateTime.Now;
var tokenDescriptor = new SecurityTokenDescriptor
{
Expires = DateTime.Now.AddMinutes(2),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
rs.Token = tokenString;
Auth filter to validate token:
public void OnAuthorization(AuthorizationFilterContext filterContext)
{
if (!ValidateToken(filterContext.HttpContext.Request.Headers["TOKEN"]))
{
filterContext.Result = new UnauthorizedResult();
}
}
private bool ValidateToken(string authToken)
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = GetValidationParameters();
SecurityToken validatedToken;
IPrincipal principal = tokenHandler.ValidateToken(authToken, validationParameters, out validatedToken);
return true;
}
catch(Exception ex)
{
return false;
}
}
private TokenValidationParameters GetValidationParameters()
{
return new TokenValidationParameters()
{
ValidateLifetime = false, // Because there is expiration in the generated token
ValidateAudience = false, // Because there is no audiance in the generated token
ValidateIssuer = false, // Because there is no issuer in the generated token
//ValidIssuer = _appSettings.ValidIssuer,
//ValidAudience = _appSettings.ValidAudience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretkey)) // The same key as the one that generate the token
};
}
What can be the issue?
I've added token (jwt) validation to my Web-API by following the steps in this tutorial. However, when I now try to add Asp.Net Identity to my application it somehow skips the token validation.
The token generation still works fine.
The API-controller-action that I am trying to access looks like this:
// GET api/users/5
[HttpGet("{id}")]
[Authorize]
public User Get(int id)
{
return _userService.FindById(id);
}
The "Authorize" attribute doesn't seem to make any difference after adding identity to my OWIN startup file, which looks like this:
var secretKey = "secret";
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));
var audience = "aud";
var issuer = "iss";
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
ValidateIssuer = true,
ValidIssuer = issuer,
ValidateAudience = true,
ValidAudience = audience,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = tokenValidationParameters
});
app.UseStaticFiles();
app.UseIdentity(); // When adding this the token-validation above seems to stop working...
var options = new TokenProviderOptions
{
Audience = audience,
Issuer = issuer,
SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256)
};
app.UseMiddleware<TokenProviderMiddleware>(Options.Create(options));
app.UseMvc();
I've tried moving the Identity middleware above JwtBearerAuth, with no luck.
It always seems to skip validating my token, and even if I leave out the token I am able to reach the action, but when I remove the Identity middleware the token-validation starts working again.