Alex Alex - 2 months ago 161
C# Question

Cannot validate token in UseJwtBearerAuthentication. Authorization has been denied

Using a single asp.net(4.6.1) web project, apparently I'm unable to validate the jwt token that was generated on the same server.

Startup.cs:

var secret = Encoding.UTF8.GetBytes("12341234123412341234");
var jwtFormatter = new CustomJwtFormat("Any", "local", secret);

// This part checks the tokens
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ExternalBearer,
AuthenticationMode = AuthenticationMode.Active, // Block requests
AllowedAudiences = new []{"Any"},
TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = new InMemorySymmetricSecurityKey(secret),
ValidAudience = "Any",
ValidIssuer = "local"
}
});

// This part issues tokens
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = false,
TokenEndpointPath = new PathString("/auth"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new CustomOAuthProvider(),
AccessTokenFormat = jwtFormatter,
RefreshTokenFormat = jwtFormatter

});

app.UseWebApi(config);


The class that generates the tokens looks like

public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
{
private readonly string _allowedAudience;
private readonly string _issuer;
private readonly byte[] _jwtTokenSignKey;

public CustomJwtFormat(string allowedAudience, string issuer, byte[] jwtTokenSignKey)
{
_allowedAudience = allowedAudience;
_issuer = issuer;
_jwtTokenSignKey = jwtTokenSignKey;
}

public string Protect(AuthenticationTicket data)
{
if (data == null) throw new ArgumentNullException(nameof(data));

var signingCredentials = new SigningCredentials
(
new InMemorySymmetricSecurityKey(_jwtTokenSignKey),
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
"http://www.w3.org/2001/04/xmlenc#sha256"
);

return new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(
_issuer,
_allowedAudience,
data.Identity.Claims,
DateTime.UtcNow, DateTime.UtcNow.AddMinutes(10),
signingCredentials
));

}

public AuthenticationTicket Unprotect(string protectedText)
{
throw new NotImplementedException();
}
}


The tokens I receive from
/auth
look valid and pass the debugger on
jwt.io
(without marking base64 for signature)
image

However
UseJwtBearerAuthentication
refuses to validate the token.
image

What could be the possible reason for this ?

Moreover, I've tried manually validating the same token in a controller without
[Authorize]
and it would perfectly validate:

var t = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEiLCJpc3MiOiJsb2NhbCIsImF1ZCI6IkFueSIsImV4cCI6MTQ3MjkxMDcwMSwibmJmIjoxNDcyOTEwMTAxfQ.ipSrRSGmje7wfzERsd-M1IDFJnN99AIC4Hs7YX4FIeI";
var TokenHandler = new JwtSecurityTokenHandler();;
var key = Encoding.UTF8.GetBytes("12341234123412341234");
SecurityToken validatedToken;
TokenValidationParameters paras = new TokenValidationParameters()
{
IssuerSigningKey = new InMemorySymmetricSecurityKey(key),
ValidAudience = "Any",
ValidIssuer = "local"
};
TokenHandler.ValidateToken(t, paras, out validatedToken);


Owin 3.0.1.0
System.IdentityModel.Tokens.Jwt 4.0.3.308261200

Answer

The problem wasn't in the token validation, but rather the that the claims were not passed on to Thread.CurrentPrincipal that the [Authorize] attribute was reading from.

In webapi config:

config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(DefaultAuthenticationTypes.ExternalBearer));

In startup config:

app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
    AuthenticationType = DefaultAuthenticationTypes.ExternalBearer,
    ...
});

app.UseJwtBearerAuthentication1(new JwtBearerAuthenticationOptions()
{
    AuthenticationType = DefaultAuthenticationTypes.ExternalBearer,
    ..
});

In GrantResourceOwnerCredentials of the OAuthAuthorizationServerProvider:
use the same authentication type, which you can read from context.Options

var identity = new ClaimsIdentity(youClaimsList, context.Options.AuthenticationType);
context.Validated(identity);

And ensure all four places have the same string as AuthenticationType. If the HostAuthenticationFilter will have a different authenticationType as input, it will not pass on the claims from owin to webapi.

Comments