I am very new to .NET / C#.
I am using jwt for means of authentication. I have this object:
var token = new JwtSecurityToken(
issuer: "example.com",
audience: "example.com",
claims: claims,
expires: DateTime.Now.AddDays(TOKEN_EXPIRY_DAYS),
signingCredentials: creds
);
However, I manually add a "created" property:
token.Payload["created"] = DateTimeOffset.Now.ToUnixTimeSeconds();
All I need to do is to write a test that checks to see if the token object has an attribute called "created".
I have tried something like this:
tokenResponse = JsonConvert.DeserializeObject<TokenResponse>(
await response.Content.ReadAsStringAsync()
);
Assert.NotEmpty(tokenResponse.token["created"]);
But this fails. Any ideas?
Related
I am newbie in JWT access Token generation. I have Public Key, Private key and ClientID. I need to generate Client_Assertion.
client_assertion: JWT (signed by client ID, public certificate and private key using
RS256 as the signature algorithm).
I have found some Node.JS code but I want to do it using .Net Framework (Not .Net Core)
Node.Js code can be seen on this link
I need to do it in C#. From where to start and how to achieve it?
I used the .NET libraries
System.IdentityModel.Tokens.Jwt
Microsoft.IdentityModel.Tokens
Microsoft.IdentityModel.JsonWebTokens
I'm also assuming that, as you said you have a private key, and that you've loaded that into an RSACryptoServiceProvider
Here's my sample code
First create your claims. This is your JWT payload
var claims = new Claim[]
{
new Claim(MicrosoftJwt.JwtRegisteredClaimNames.Sub, "your subject"),
new Claim(MicrosoftJwt.JwtRegisteredClaimNames.Iat, DateTime.Now.ToEpochSeconds().ToString(), ClaimValueTypes.Integer),
new Claim(MicrosoftJwt.JwtRegisteredClaimNames.Exp, DateTime.Now.AddMinutes(60).ToEpochSeconds().ToString(), ClaimValueTypes.Integer),
};
Then you need to configure your signing credentials using your private key.
// Assuming you already have your key loaded into an RSACryptoServiceProvider
var key = new MicrosoftTokens.RsaSecurityKey(csp)
var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.RsaSha256);
Now you can create your token
var jwt = new JwtSecurityToken(
issuer : issuer,
audience : audience,
claims : claims,
signingCredentials: signingCredentials
);
// You can add extra items to your Header if necessary
jwt.Header.Add("kid", deviceId);
You can then write your token to a string
var assertion = new JwtSecurityTokenHandler().WriteToken(jwt);
I think the value of assertion is what you're trying to get
I'm working on a small side-project API and I wanted to implement JWT authentication. I already made some API with JWT and always made custom implementation and validation.
This time, I wanted to use Identity/Entity Framework to go faster and to use the tools that are already made for me.
While doing the GenerateToken method and looking through the internet, I noticed that a lot of the tables created by IdentityFramework are not used. And I got interested in AspNetUserToken.
I noticed that instead of
private object GenerateToken(IdentityUser user)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.UTF8.GetBytes(ApiConfig.JwtSecretKey);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.Email, user.Email),
}),
Expires = DateTime.UtcNow.AddSeconds(double.Parse(ApiConfig.JwtExp)), //TODO: Try parse
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
Audience = ApiConfig.JwtAudience,
Issuer = ApiConfig.JwtIssuer
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
Which I used to generate a new JWT token, add the claims, issuer, audience, etc... Could maybe be replaced by this :
//Removes existing token
_userManager.RemoveAuthenticationTokenAsync(user, "lboard", "login");
//Creates a new one
var newToken = await _userManager.GenerateUserTokenAsync(user, "lboard", "login");
//Set the new token for the user
await _userManager.SetAuthenticationTokenAsync(user, "lboard", "login", newToken);
I would like to know what are the differences between the two methods, and if there are any benefits of using a custom implementation or if I'm better off with the IdentityFramework one.
The GenerateUserTokenAsync methods is used internally by other UserManager methods like GenerateChangeEmailTokenAsync, GenerateChangePhoneNumberTokenAsync and so on. REF
In order to use more abstract GenerateUserTokenAsync, you must provide a token provider that actually generates the token. Since you don't have any default token providers for a JWT access token, you would still have to write the logic yourself and register your custom token provider and then you could use the GenerateUserTokenAsync method.
You would still need to write the JWT logic by yourself, incude claims etc, but with an added overhead.
We have several tests that that generate a jwt request to call a server to retrieve a token. We have 6 tests that make the same call to the same method using the same data. Here is the method:
'''
private static string GenerateSignedTokenRequest(
string privateKey,
string privateKeyPass,
string clientID,
string audience,
int lifetime)
{
var jti = Guid.NewGuid().ToString();
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Jti, jti),
new Claim(JwtRegisteredClaimNames.Sub, clientID),
};
var decodedKey = DecodeRsaPrivateKeyFromPem(
privateKey,
privateKeyPass);
var priDecKey = decodedKey.Private as RsaPrivateCrtKeyParameters;
var rsaParams = DotNetUtilities.ToRSAParameters(priDecKey);
using (var rsa = RSA.Create(rsaParams))
{
var token = new JwtSecurityToken(
clientID,
audience,
claims,
DateTime.Now.AddMinutes(-1),
DateTime.Now.AddSeconds(lifetime),
new SigningCredentials(
new RsaSecurityKey(rsa),
SecurityAlgorithms.RsaSha256));
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
'''
We get the following error on every other test that runs on the WriteToken(token) method:
{"Cannot access a disposed object.\r\nObject name: 'RSA'."}
What is baffling is each odd number test runs through this code fine but each even number test fails. But when I rerun each test individually they are all green. It is only when I run them all together that every other test fails.
This has happened when moving from .Net Core and Test frameworks from 3.1.0 to 3.1.4
So It seems the issue was the upgrade of Windows Azure Active Directory IdentityModel Extensions for .Net. It seems there is a cache that not is affected by putting a using around the RSA.Create() method. By removing the using all the tests are green.
here are a few links that helped my diagnose:
https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc
And:
https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/1433
Update this
new SigningCredentials(
new RsaSecurityKey(rsa),
SecurityAlgorithms.RsaSha256)
To
new SigningCredentials(
new RsaSecurityKey(rsa),
SecurityAlgorithms.RsaSha256){
CryptoProviderFactory = new CryptoProviderFactory { CacheSignatureProviders = false }
}
reference : https://vmsdurano.com/-net-core-3-1-signing-jwt-with-rsa/
Below code works perfectly fine for me.
In the below code, I am taking claims as parameter and pulling certificate from configuration. Instead of setting private key to a variable, putting directly in key fixed the issue.
using (X509Certificate2 certificate = new X509Certificate2(certPath, tokenConfig["CertSecret"], System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.PersistKeySet))
{
if (certificate.HasPrivateKey)
{
var signingCredentials = new SigningCredentials(
key: new RsaSecurityKey((RSA)certificate.PrivateKey),
algorithm: SecurityAlgorithms.RsaSha256
);
var token = new JwtSecurityToken(
claims: claims,
expires: DateTime.Now.AddMinutes(Convert.ToInt32(configuration["TokenMinutesToExpire"])),
signingCredentials: signingCredentials);
return $"Bearer {new JwtSecurityTokenHandler().WriteToken(token)}";
}
}
}
I am using
var payload = new JwtPayload(issuer, audience, claim identities, DateTime.Now, DateTime.Now.AddMinutes(60), DateTime.Now);
to generate auth token having 60-minute expiring time but it is expiring before the expiring time
I use the following method to build a token in my asp.net core web api service:
private string BuildToken()
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtOptions.Key));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
_jwtOptions.Issuer, // some issuer, e.x. you can specify your localhost
_jwtOptions.Issuer,
expires: DateTime.Now.AddMinutes(_jwtOptions.Expires), // int value
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
}
Perhaps this helps you somehow.
I have a login method, which give me the access and refresh token.
[HttpGet]
[AllowAnonymous]
public IActionResult Login()
{
var claims = new Claim[]
{
new Claim(ClaimTypes.Role, "Administrator")
};
var now = DateTime.UtcNow;
var signingCredentials = new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(this.configuration["Key"])), SecurityAlgorithms.HmacSha256);
var accessToken = new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(
claims: claims,
notBefore: now,
expires: now.AddMinutes(10),
signingCredentials: signingCredentials));
var refreshToken = new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(
notBefore: now,
expires: now.AddYears(2),
signingCredentials: signingCredentials));
return Ok(new JwtToken
{
AccessToken = accessToken,
RefreshToken = refreshToken
});
}
And I use the access token with Postman. In the headers:
Bearer eyJhbGciOiJIUzI1...
But after 10 minutes I can't use the API because the access token is rejected. How can I renew the access token with every request to the API (within these 10 minutes)?
You could set a variable or cookie with the expiry time of the token, then every request that is made you need to check if this expiry is in the past. If it is, you should be able to use the refresh token to get a new access token.
This will ensure you are not getting a token for each request but only when the token expires.