JianYA
JianYA

Reputation: 3024

Jwt Unable to valid issuer or audience

This is my token decoder. When I try to decode it, my principal ends up being null thus leading to this error:

'IDX10208: Unable to validate audience. validationParameters.ValidAudience is null or whitespace and validationParameters.ValidAudiences is null.'

When I decode my token to check

"nbf": 1539167980, "exp": 1539168580, "iat": 1539167980, "iss": "http://localhost:55260", "aud": "http://localhost:55260"

This is the host that my token generator runs on as well. Why is the principal causing issues?

public class DecodeToken
{
    private IConfiguration configuration;
    public DecodeToken(IConfiguration configuration)
    {
        this.configuration = configuration;
    }

    public AuthenticationDto Decode(String Input)
    {
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JwtAuthentication:SecurityKey"]));
        var handler = new JwtSecurityTokenHandler();
        var tokenSecure = handler.ReadToken(Input) as SecurityToken;
        var validations = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = key,
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = false
        };
        SecurityToken securityToken;
        var principal = handler.ValidateToken(Input, validations, out securityToken);
        var jwtSecurityToken = securityToken as JwtSecurityToken;
        if (jwtSecurityToken == null || !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase))
        {
            throw new SecurityTokenException("Invalid Token");
        }
        AuthenticationDto authenticationDto = new AuthenticationDto
        {
            Email = principal.Claims.Where(c => c.Type == "Email").Select(c => c.Value).SingleOrDefault(),
            UserName = principal.Claims.Where(c => c.Type == "UserName").Select(c => c.Value).SingleOrDefault(),
            FirstName = principal.Claims.Where(c => c.Type == "FirstName").Select(c => c.Value).SingleOrDefault(),
            LastName = principal.Claims.Where(c => c.Type == "LastName").Select(c => c.Value).SingleOrDefault(),
            PhoneNumber = principal.Claims.Where(c => c.Type == "PhoneNumber").Select(c => c.Value).SingleOrDefault(),
            Id = principal.Claims.Where(c => c.Type == "Id").Select(c => c.Value).SingleOrDefault(),
            ExpiryDateTime = principal.Claims.Where(c => c.Type == "exp").Select(c => c.Value).SingleOrDefault(),
            Roles = principal.Claims.Where(c => c.Type == "Roles").Select(c => c.Value).ToList(),
        };
        return authenticationDto;
    }
}

This is what my Startup.cs looks like:

services.AddAuthentication(options => {
    options.DefaultAuthenticateScheme = "Jwt";
    options.DefaultChallengeScheme = "Jwt";
})
.AddJwtBearer("Jwt", options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtAuthentication:SecurityKey"])),

        ValidateIssuer = true,
        ValidIssuer = Configuration["JwtAuthentication:Issuer"],

        ValidateAudience = true,
        ValidAudience = Configuration["JwtAuthentication:Audience"],

        ValidateLifetime = true, //validate the expiration and not before values in the token

        ClockSkew = TimeSpan.Zero //5 minute tolerance for the expiration date
    };
    options.Events = new JwtBearerEvents
    {
        OnAuthenticationFailed = context =>
        {
            if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
            {
                context.Response.Headers.Add("Token-Expired", "true");
            }
            return Task.CompletedTask;
        }
    };
});

Have I configured something wrong?

Upvotes: 13

Views: 35605

Answers (1)

Kirk Larkin
Kirk Larkin

Reputation: 93063

Your error appears to make perfect sense. In ConfigureServices, you're setting up the TokenValidationParameters so that it validates your issuer/audience and you're providing values for ValidIssuer and ValidAudience, but you're not doing the same in your Decode function, where you're only setting ValidateIssuer and ValidateAudience without setting the values that you expect. You need to configure these on your validations variable in Decode, like this:

var validations = new TokenValidationParameters
{
    ValidateIssuerSigningKey = true,
    IssuerSigningKey = key,
    ValidateIssuer = true,
    ValidateAudience = true,
    ValidateLifetime = false,
    // Add these...
    ValidIssuer = configuration["JwtAuthentication:Issuer"],
    ValidAudience = configuration["JwtAuthentication:Audience"]
};

Upvotes: 26

Related Questions