AzureAD / azure-activedirectory-identitymodel-extensions-for-dotnet

IdentityModel extensions for .Net
MIT License
1.06k stars 401 forks source link

Discussion: Microsoft.AspNetCore.Authentication.JwtBearer contains absurd amount of black magic #493

Closed Tratcher closed 8 years ago

Tratcher commented 8 years ago

From @ryanelian on August 28, 2016 14:56

I am trying to develop a ASP.NET application using JWT for securing certain routes and then decided that I would use app.UseJwtBearerAuthentication() to make my life easier by not reinventing the wheel. (And so I can use [Authorize] and call it a day)

However, instead of speeding up an entire day of work, I ended up investigating for hours why a perfectly fine Authorization: Bearer insert_jwt_token_here results in 401 Unauthorized response from the server.

The token in question was generated using the trusty jose-jwt and successfully parsed and verified using jwt.io and the jose-jwt itself so I'm quite confident that the token is correct.

After hours of browsing and reading blogs with no avail, I decided to download the source from Release branch directly and perform step by step debugging by putting a breakpoint in JwtBearerHandler. (Curiously, Microsoft.AspNetCore.Authentication failed to compile unless I enable the nightly myget feed for alpha 1.1.0, because Microsoft.Extensions.SecurityHelper.Sources 1.0.0 doesn't exist in NuGet.)

Surprisingly, the token in question was failed to be validated by the signature validator because of this:

IDX10503: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.SymmetricSecurityKey , KeyId: 
'.
Exceptions caught:
 'System.ArgumentOutOfRangeException: IDX10603: The algorithm: 'HS256' cannot have less than: '128' bits. KeySize is: '112'.
Parameter name: key.KeySize
   at Microsoft.IdentityModel.Tokens.SymmetricSignatureProvider..ctor(SecurityKey key, String algorithm)
   at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures)
   at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateForVerifying(SecurityKey key, String algorithm)
   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: '{"alg":"HS256","typ":"JWT"}.{"iss":"TAM.Passport","sub":"ryanelian","iat":1472392366,"exp":1472393266,"jti":"9edb6556-dfa8-4d3e-99ee-4517e986bd01","FirstName":"Ryan","LastName":"Elian","Email":"ryan@email.com","EmployeeId":"12345678","Roles":["Administrator","Programmer"]}'

Apparently, my development secret key ACCELISTROCKS! was too short according to the validator. This is quite an undocumented surprise. So I ended up increasing the secret key to ACCELISTROCKSBOOMBOOMFIRE! and the validator works.

However, the token failed to be validated once again:

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

   at Microsoft.IdentityModel.Tokens.Validators.ValidateAudience(IEnumerable`1 audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateAudience(IEnumerable`1 audiences, JwtSecurityToken securityToken, TokenValidationParameters validationParameters)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
   at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.<HandleAuthenticateAsync>d__1.MoveNext() in D:\VS\TAM.Passport\src\Microsoft.AspNetCore.Authentication.JwtBearer\JwtBearerHandler.cs:line 100

I didn't remember asking the TokenValidationParameters to validate Audience...

var tokenValidationParameters = new TokenValidationParameters
{
    ValidateIssuerSigningKey = true,
    IssuerSigningKey = new SymmetricSecurityKey(this.TokenSecretKey),

    ValidateIssuer = true,
    ValidIssuer = TokenService.Issuer,

    ValidateLifetime = true,
    ClockSkew = TimeSpan.Zero
};

app.UseJwtBearerAuthentication(new JwtBearerOptions
{
    AutomaticChallenge = true,
    AutomaticAuthenticate = true,
    TokenValidationParameters = tokenValidationParameters,
});

So I ended up putting these into the TokenValidationParameters for good measure:

ValidateAudience = false,
ValidateActor = false,

After I did that, the authorization finally works as intended.

I hope this issue can serve as a mini-blog for other people who are scratching their head over this issue.

Copied from original issue: aspnet/Security#959

brentschmaltz commented 8 years ago

@ryanelian @Tratcher The library was designed to be secure by default. That means a few things: https for metadata retrieval, tokens signed, key sizes of sufficient length, issuer validation, audience validation, expiration times checked, nonce checks, c_hash and at_hash checks, etc. We want to make sure that if someone turns off say, AudienceValidation {which opens up a forwarding attack}, they do so with their eyes open.

That said integration with asp.net core 1.0 could be better. We could relax some items if say env.IsDevelopmentEnvironment() was true.

It looks like we could help users by improving our docs. Thanks for taking the time to detail your experience. Stackoverflow would benefit from such a question.

ryanelian commented 8 years ago

That said integration with asp.net core 1.0 could be better. We could relax some items if say env.IsDevelopmentEnvironment() was true.

Maybe instead of that, if in development make it so it can log or emit message containing the reason that the authorization failed? That'd help tremendously.

It looks like we could help users by improving our docs.

Please do! That'd be a godsend.

When I Google UseJwtBearerAuthentication, the only relevant results that appear is this:

https://stormpath.com/blog/token-authentication-asp-net-core

I would greatly appreciate it if there are documentations in http://docs.asp.net/ regarding the security middlewares.

leastprivilege commented 8 years ago

That said integration with asp.net core 1.0 could be better. We could relax some items if say env.IsDevelopmentEnvironment() was true.

That seems like a really bad idea to me.

Maybe if in development, make it so it can log or emit message containing the reason that the authorization failed? That'd help tremendously.

Doesn't it do that via logging already?

ryanelian commented 8 years ago

Doesn't it do that via logging already?

I'm not aware of that. I will check later. Thank you for telling me.

brentschmaltz commented 8 years ago

@leastprivilege I am thinking only about relaxing https for now not requirements such as 'aud' etc. I should have been more specific. I am finding that in 'dev' mode sometimes https gets in the way.

brentschmaltz commented 8 years ago

I think we have what we need from this thread.

Adewagold commented 6 years ago

Thanks so much.

fasas1 commented 1 year ago

Thanks