Reputation: 203
I'm developing an ASP.NET Core 2 app using Identity and Sustainsys.Saml2 (for SAML auth). I've made the necessary configurations in the Startup.cs file. Now when I run the project and try to login using the SAML2 (as an external login), I get the following error after enter my credentials:
SecurityTokenInvalidAudienceException: IDX10214: Audience validation failed. Audiences: '[PII is hidden]'. Did not match: validationParameters.ValidAudience: '[PII is hidden]' or validationParameters.ValidAudiences: '[PII is hidden]'. Microsoft.IdentityModel.Tokens.Validators.ValidateAudience(IEnumerable audiences, SecurityToken securityToken, TokenValidationParameters validationParameters) Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateConditions(Saml2SecurityToken samlToken, TokenValidationParameters validationParameters) Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateToken(string token, TokenValidationParameters validationParameters, out SecurityToken validatedToken) Sustainsys.Saml2.Saml2P.Saml2Response+d__60.MoveNext() System.Collections.Generic.List..ctor(IEnumerable collection) System.Linq.Enumerable.ToList(IEnumerable source) Sustainsys.Saml2.Saml2P.Saml2Response.GetClaims(IOptions options, IDictionary relayData) Sustainsys.Saml2.WebSso.AcsCommand.ProcessResponse(IOptions options, Saml2Response samlResponse, StoredRequestState storedRequestState) Sustainsys.Saml2.WebSso.AcsCommand.Run(HttpRequestData request, IOptions options) Sustainsys.Saml2.AspNetCore2.Saml2Handler+d__12.MoveNext() System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) Microsoft.AspNetCore.Authentication.AuthenticationMiddleware+d__6.MoveNext() System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.MigrationsEndPointMiddleware+d__4.MoveNext() System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware+d__6.MoveNext() System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware+d__6.MoveNext() System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware+d__7.MoveNext()
I'm not understanding what does this mean. Am I missing something?
Here's what I have in the Startup file
services.AddAuthentication()
.AddSaml2(options =>
{
var spOptions = new SPOptions
{
EntityId = new EntityId("https://localhost:44373/Saml2"),
ReturnUrl = new Uri("https://localhost:44373"),
MinIncomingSigningAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
};
options.SPOptions = spOptions;
options.IdentityProviders.Add(new IdentityProvider(new EntityId("https://www.example.com/SSO/SAML/App"), options.SPOptions)
{
AllowUnsolicitedAuthnResponse = false,
MetadataLocation = "https://www.example.com/SSO/SAMLMetadata/App",
LoadMetadata = true,
});
});
Thanks in advance...
Upvotes: 15
Views: 63688
Reputation: 4233
in my case the issuer and audience for the JwtSecurityToken were omitted. In my derived class UserService: IUserService I defined the issuer and audience variables in the function generateJwtToken. They variables must match services.AddJwtBearer variables in the startup.csv for ValidIssuer and ValidAudience. see(https://dotnetcoretutorials.com/2020/01/15/creating-and-validating-jwt-tokens-in-asp-net-core/).
quote:
The Issuer and Audience are funny things because realistically, you probably won’t have a lot of use for them. Issuer is “who” created this token, for example your website, and Audience is “who” the token is supposed to be read by. So a good example might be that when a user logs in, your authentication api (auth.mywebsite.com) would be the issuer, but your general purposes API is the expected audience (api.mywebsite.com). These are actually free text fields so they don’t have to be anything in particular, but later on when we validate the issuer/audience, we will need to know what they are.
public class UserService : IUserService {
private string generateJwtToken(long userId)
{
var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_appSettings.Secret));
var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
var tokenOptions = new JwtSecurityToken(
issuer: "http://localhost:5000",
audience: "http://localhost:5000",
claims: new List<Claim> {
new Claim(ClaimTypes.Name, userId.ToString()),
new Claim(ClaimTypes.Role, "Operator")
},
expires: DateTime.UtcNow.AddDays(7),
signingCredentials: signinCredentials
);
var tokenString = new JwtSecurityTokenHandler().WriteToken(tokenOptions);
return tokenString;
}
}
startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(opt =>
{
opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(opt =>
{
opt.RequireHttpsMetadata = false;
opt.SaveToken = true;
opt.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidIssuer = "http://localhost:5000",
ValidAudience = "http://localhost:5000"
};
});
}
Upvotes: 0
Reputation: 3147
IDX10214
: Check this section if you are using Microsoft.Identity.Web
version 1.4.1 or similar and you get this exception (literally copied, and you have to change the log levels in appsettings.json
to get to see this):
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[1]
Failed to validate the token.
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidAudienceException: IDX10214: Audience validation failed. Audiences: 'System.String'. Did not match: validationParameters.ValidAudience: 'System.String' or validationParameters.ValidAudiences: 'System.String'.
at Microsoft.IdentityModel.Tokens.Validators.ValidateAudience(IEnumerable`1 audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)
at Microsoft.Identity.Web.Resource.RegisterValidAudience.ValidateAudience(IEnumerable`1 audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)
at Microsoft.IdentityModel.Tokens.Validators.ValidateAudience(IEnumerable`1 audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateAudience(IEnumerable`1 audiences, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateTokenPayload(JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
Would be so much more helpful to actually see the value of those System.String
's. And if it didn't matter whether you use a GUID or something memorizable and readable.
Essentially you have to add the Audience
property to appsettings.json
and that must be equal to the Application ID URI
in the Azure portal. I had no luck whatsoever hacking the ClientId
and have that match the Application ID URI
. That isn't the final solution to this - AFAIK that must still be equal to the Application (client) ID
in the Azure portal, i.e. a GUID without any prefixes or suffixes.
Upvotes: 4
Reputation: 666
As far as I know, this error clearly states that audience that came in your SAML-token is different from the value in your Startup configuration. It might be helpful to compare these values. Sometimes the validation fails due to case-sensitive comparison, so you should pay attention in which case your audiencies are in token and configuration.
According to the source code (Saml2Response) and as Anders Abel pointed out, ValidAudience
property is initialized from SPOptions.EntityId
that you configure here:
var spOptions = new SPOptions
{
EntityId = new EntityId("https://localhost:44373/Saml2"),
ReturnUrl = new Uri("https://localhost:44373"),
MinIncomingSigningAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
};
So you should compare the EntityId
value, that you have configured with the value in your saml-token, which might look like this:
<saml:Audience>The value here should be the same as in your startup configuration</saml:Audience>
Upvotes: 19