The following code gives me Azure AD security token, I need to validate that token is valid or not. How to achieve this?
// Get OAuth token using client credentials
string tenantName = "mytest.onmicrosoft.com";
string authString = "https://login.microsoftonline.com/" + tenantName;
AuthenticationContext authenticationContext = new AuthenticationContext(authString, false);
// Config for OAuth client credentials
string clientId = "fffff33-6666-4888-a4tt-fbttt44444";
string key = "123v47o=";
ClientCredential clientCred = new ClientCredential(clientId, key);
string resource = "http://mytest.westus.cloudapp.azure.com";
string token;
Task<AuthenticationResult> authenticationResult = authenticationContext.AcquireTokenAsync(resource, clientCred);
token = authenticationResult.Result.AccessToken;
Console.WriteLine(token);
// How can I validate this token inside my service?
There are two steps to verify the token. First, verify the signature of the token to ensure the token was issued by Azure Active Directory. Second, verify the claims in the token based on the business logic.
For example, we need to verify the iss and aud claim if you were developing a single tenant app. And you also need to verify the nbf to ensure the token is not expired. For more claims you can refer here.
Below description is from here about the detail of signature verifying. (Note: The example below uses the Azure AD v2 endpoint. You should use the endpoint that corresponds to the endpoint the client app is using.)
The access token from the Azure AD is a JSON Web Token(JWT) which is signed by Security Token Service in private key.
The JWT includes 3 parts: header, data, and signature. Technically, we can use the public key to validate the access token.
First step – retrieve and cache the signing tokens (public key)
Endpoint: https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration
Then we can use the JwtSecurityTokenHandler to verify the token using the sample code below:
public JwtSecurityToken Validate(string token)
{
string stsDiscoveryEndpoint = "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration";
ConfigurationManager<OpenIdConnectConfiguration> configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint);
OpenIdConnectConfiguration config = configManager.GetConfigurationAsync().Result;
TokenValidationParameters validationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
IssuerSigningTokens = config.SigningTokens,
ValidateLifetime = false
};
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
SecurityToken jwt;
var result = tokenHandler.ValidateToken(token, validationParameters, out jwt);
return jwt as JwtSecurityToken;
}
And if you were using the OWIN components in your project, it is more easy to verify the token. We can use the code below to verify the token:
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = ConfigurationManager.AppSettings["ida:Audience"],
Tenant = ConfigurationManager.AppSettings["ida:Tenant"]
});
Then we can use the code below to verify the ‘scope’ in the token:
public IEnumerable<TodoItem> Get()
{
// user_impersonation is the default permission exposed by applications in AAD
if (ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/scope").Value != "user_impersonation")
{
throw new HttpResponseException(new HttpResponseMessage {
StatusCode = HttpStatusCode.Unauthorized,
ReasonPhrase = "The Scope claim does not contain 'user_impersonation' or scope claim not found"
});
}
...
}
And here is a code sample which protected the web API with Azure AD:
Protect a Web API using Bearer tokens from Azure AD
Just wanted to add to Fei's answer for people using .net Core 2.0
You'll have to modify 2 lines of the Validate(string token) method.
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(
stsDiscoveryEndpoint,
new OpenIdConnectConfigurationRetriever()); //1. need the 'new OpenIdConnect...'
OpenIdConnectConfiguration config = configManager.GetConfigurationAsync().Result;
TokenValidationParameters validationParameters = new TokenValidationParameters
{
//decode the JWT to see what these values should be
ValidAudience = "some audience",
ValidIssuer = "some issuer",
ValidateAudience = true,
ValidateIssuer = true,
IssuerSigningKeys = config.SigningKeys, //2. .NET Core equivalent is "IssuerSigningKeys" and "SigningKeys"
ValidateLifetime = true
};
But if you are not using OWIN in your projects, it is going to be a little hard or at least time consuming..
This articleHere is great resource.
And because I do not have much to add on the above, except the detailed code.. Here is something that can be useful to you:
public async Task<ClaimsPrincipal> CreatePrincipleAsync()
{
AzureActiveDirectoryToken azureToken = Token.FromJsonString<AzureActiveDirectoryToken>();
var allParts = azureToken.IdToken.Split(".");
var header = allParts[0];
var payload = allParts[1];
var idToken = payload.ToBytesFromBase64URLString().ToAscii().FromJsonString<AzureActiveDirectoryIdToken>();
allParts = azureToken.AccessToken.Split(".");
header = allParts[0];
payload = allParts[1];
var signature = allParts[2];
var accessToken = payload.ToBytesFromBase64URLString().ToAscii().FromJsonString<AzureActiveDirectoryAccessToken>();
var accessTokenHeader = header.ToBytesFromBase64URLString().ToAscii().FromJsonString<AzureTokenHeader>();
var isValid = await ValidateToken(accessTokenHeader.kid, header, payload, signature);
if (!isValid)
{
throw new SecurityException("Token can not be validated");
}
var principal = await CreatePrincipalAsync(accessToken, idToken);
return principal;
}
private async Task<bool> ValidateToken(string kid, string header, string payload, string signature)
{
string keysAsString = null;
const string microsoftKeysUrl = "https://login.microsoftonline.com/common/discovery/keys";
using (var client = new HttpClient())
{
keysAsString = await client.GetStringAsync(microsoftKeysUrl);
}
var azureKeys = keysAsString.FromJsonString<MicrosoftConfigurationKeys>();
var signatureKeyIdentifier = azureKeys.Keys.FirstOrDefault(key => key.kid.Equals(kid));
if (signatureKeyIdentifier.IsNotNull())
{
var signatureKey = signatureKeyIdentifier.x5c.First();
var certificate = new X509Certificate2(signatureKey.ToBytesFromBase64URLString());
var rsa = certificate.GetRSAPublicKey();
var data = string.Format("{0}.{1}", header, payload).ToBytes();
var isValidSignature = rsa.VerifyData(data, signature.ToBytesFromBase64URLString(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return isValidSignature;
}
return false;
}
There are some functions that I use in here that are not available for you, they are self descriptive.
Related
EDIT: Issue solved.I am able to validate jwts signature against the public key of the x5c cert chain. Thank you, Topaco, your comment lead me to check my data once again. Solution on the bottom.
Im new on this topic, but Im trying to implement a jwt signature verification and dont want to use bouncy castle. The SigningCert(Chain) lies in the JWT's 'x5c' header. jwt-alg header says "bp256r1".
My problem is Im getting following ArgumentException as Validation Result:
IDX10645: Elliptical Curve not supported for curveId: 'brainpoolP256r1'
To be fair: That message doesnt leave much room for interpretation. But I thought, that with Win10 and Ongoing, BP256R1 algorithm is supported?.
What am i Missing?
thats my Code, I'd wish validation result is true, but it inst:
public async Task<JwtSecurityToken> GetDiscoveryDocument()
{
//Get Token
var response = await _client.GetAsync(".well-known/openid-configuration");
response.EnsureSuccessStatusCode();
//parse Token
string token = await response.Content.ReadAsStringAsync();
var jwtHandler = new JwtSecurityTokenHandler();
var jwt = jwtHandler.ReadJwtToken(token);
//Get SigningCert
var x5cCerts = JsonSerializer.Deserialize<object[]>(jwt.Header["x5c"].ToString());
X509Certificate2 cert = new X509Certificate2(Encoding.UTF8.GetBytes(x5cCerts.First().ToString()));
//Get PublicKey and Set validationParameters, ValidateSignature
var ecdsa = cert.GetECDsaPublicKey();
var vaidationResult = await jwtHandler.ValidateTokenAsync(token, new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new ECDsaSecurityKey(ecdsa),
});
return jwt;
}
I also tried to use:
var ecdsa = cert.GetECDsaPublicKey();
byte[] data = ....;
byte[] signature = ....;
ecdsa.VerifyData(data, signature, HashAlgorithmName.SHA256);
Solution:
public async Task<JwtSecurityToken> GetDiscoveryDocument()
{
//Get Token
var response = await _client.GetAsync(".well-known/openid-configuration");
response.EnsureSuccessStatusCode();
//parse Token
string token = await response.Content.ReadAsStringAsync();
var jwtHandler = new JwtSecurityTokenHandler();
var jwt = jwtHandler.ReadJwtToken(token);
//Get SigningCert
var x5cCerts = JsonSerializer.Deserialize<object[]>(jwt.Header["x5c"].ToString());
X509Certificate2 cert = new X509Certificate2(Encoding.UTF8.GetBytes(x5cCerts.First().ToString()));
//Get PublicKey and Set validationParameters, ValidateSignature
var ecdsa = cert.GetECDsaPublicKey();
var valid = ecdsa.VerifyData(Encoding.UTF8.GetBytes(jwt.EncodedHeader + "." + jwt.EncodedPayload), Base64UrlEncoder.DecodeBytes(jwt.RawSignature), HashAlgorithmName.SHA256);
return jwt;
}
I have a website where users log in on an external website and are redirected back with a token in the querystring. I then validate the token and create the authentication ticket. How would I check on subsequent requests that the user is now logged in? And how would I log a user out?
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var token = Request.Query["token"].FirstOrDefault();
if (token != null)
{
var validator = new JwtSecurityTokenHandler();
var region = "us-xxx-x";
var userPoolId = "us-xxx-xxxxxxx";
var appClientId = "xxxxxxxxxxxxxxxxxxxx";
var cognitoIssuer = $"https://cognito-idp.{region}.amazonaws.com/{userPoolId}";
var jwtKeySetUrl = $"{cognitoIssuer}/.well-known/jwks.json";
var validationParameters = new TokenValidationParameters
{
IssuerSigningKeyResolver = (s, securityToken, identifier, parameters) =>
{
var json = new WebClient().DownloadString(jwtKeySetUrl);
var keys = JsonConvert.DeserializeObject<JsonWebKeySet>(json).Keys;
return (IEnumerable<SecurityKey>)keys;
},
ValidIssuer = cognitoIssuer,
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidateLifetime = true,
ValidAudience = appClientId
};
// validate the token
var principal = validator.ValidateToken(token, validationParameters, out var validatedToken);
if (principal.HasClaim(c => c.Type == ClaimTypes.NameIdentifier))
{
var claims = new[] { new Claim("token", token) };
var identity = new ClaimsIdentity(claims, nameof(TokenAuthenticationHandler));
var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), this.Scheme.Name);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
else
return Task.FromResult(AuthenticateResult.Fail("Token validation failed"));
}
else
return Task.FromResult(AuthenticateResult.Fail("No token"));
}
I tried with the code:
In controller:
[Authorize(AuthenticationSchemes = "Cookies,Bearer")]
public IActionResult Index()
{
return View();
}
public IActionResult CreateToken()
{
var claims = new Claim[]
{
new Claim(ClaimTypes.Name, "Jeffcky"),
};
var token = GenerateToken(claims);
Response.Cookies.Append("x-access-token", token);
return RedirectToAction("Login");
}
private string GenerateToken(Claim[] claims)
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("1234567890123456"));
var token = new JwtSecurityToken(
issuer: "http://localhost:5000",
audience: "http://localhost:5001",
claims: claims,
notBefore: DateTime.Now,
expires: DateTime.Now.AddMinutes(5),
signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
[HttpPost]
public async Task<IActionResult> Login(User user)
{
var token = Request.Cookies["x-access-token"];
var jsonToken = new JwtSecurityTokenHandler().ReadToken(token) as JwtSecurityToken;
var username = jsonToken.Claims.FirstOrDefault(m => m.Type == ClaimTypes.Name).Value;
if (username!=user.Name)
{
var claims = new Claim[]
{
new Claim(ClaimTypes.Name, "Jeffcky"),
};
var claimsIdentity = new ClaimsIdentity(claims, "Login");
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity));
}
return RedirectToAction("Index");
}
In startup:
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie( m =>
{
m.LoginPath = new Microsoft.AspNetCore.Http.PathString("/Home/Login");
m.AccessDeniedPath = new Microsoft.AspNetCore.Http.PathString("....");
m.SlidingExpiration = true;
m.ExpireTimeSpan = TimeSpan.FromMinutes(120);
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("1234567890123456")),
ValidateIssuer = true,
ValidIssuer = "http://localhost:5000",
ValidateAudience = true,
ValidAudience = "http://localhost:5001",
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(5)
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Cookies["x-access-token"];
if (!string.IsNullOrEmpty(accessToken))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
The Result:
you could modify the codes in login controller to pool data from database and varify the userinformation to meet your requirement.
And the Attribute[Authorize(AuthenticationSchemes = "Cookies,Bearer")] allows you Authentication with cookie or jwt,it may help in some situation
and if you want to logout,you could delete the cookie which strores the token or ticket
UpDate:
Accroding to your description,I think you could use jwt Authentication and set in your startup as follow:
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["tookn"];
if (!string.IsNullOrEmpty(accessToken))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
Is it the result you excepted?
Full disclosure, I'm not very knowledgeable on the library you are using or on what exactly you are trying to do here, but I can tell you how these things work, generally speaking. Conceptually logging in and out on a web service works as follows:
Client sends authentication information to the server (Username, password hash, 2FA info, etc.)
The server checks the authentication information against the stored information about the user (e.g. compare password hash to the stored password hash)
If authentication is sucessful, the server creates and stores a cookie (a long randomly generated string). The server stores the cookie along with information such as the user who the cookie belongs to, and when the cookie was created.
The server sends the cookie to the client. The client is now "logged in".
Now when the client wants to send some http request to the web service, they send the cookie along with the request. The server then looks up who the cookie belongs to and handles the http request appropriately.
When the user wants to log out they send an http request to the server asking to log out, and pass the cookie along with the message. The server then invalidates the cookie, and further http requests are handled as if the user is logged out.
I don't understand how this library works. Could you help me please ?
Here is my simple code :
public void TestJwtSecurityTokenHandler()
{
var stream =
"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJJU1MiLCJzY29wZSI6Imh0dHBzOi8vbGFyaW0uZG5zY2UuZG91YW5lL2NpZWxzZXJ2aWNlL3dzIiwiYXVkIjoiaHR0cHM6Ly9kb3VhbmUuZmluYW5jZXMuZ291di5mci9vYXV0aDIvdjEiLCJpYXQiOiJcL0RhdGUoMTQ2ODM2MjU5Mzc4NClcLyJ9";
var handler = new JwtSecurityTokenHandler();
var jsonToken = handler.ReadToken(stream);
}
This is the error :
The string needs to be in compact JSON format, which is of the form: Base64UrlEncodedHeader.Base64UrlEndcodedPayload.OPTIONAL,Base64UrlEncodedSignature'.
If you copy the stream in jwt.io website, it works fine :)
I found the solution, I just forgot to Cast the result:
var stream = "[encoded jwt]";
var handler = new JwtSecurityTokenHandler();
var jsonToken = handler.ReadToken(stream);
var tokenS = jsonToken as JwtSecurityToken;
Or, without the cast:
var token = "[encoded jwt]";
var handler = new JwtSecurityTokenHandler();
var jwtSecurityToken = handler.ReadJwtToken(token);
I can get Claims using:
var jti = tokenS.Claims.First(claim => claim.Type == "jti").Value;
new JwtSecurityTokenHandler().ReadToken("") will return a SecurityToken
new JwtSecurityTokenHandler().ReadJwtToken("") will return a JwtSecurityToken
If you just change the method you are using you can avoid the cast in the above answer
You need the secret string which was used to generate encrypt token.
This code works for me:
protected string GetName(string token)
{
string secret = "this is a string used for encrypt and decrypt token";
var key = Encoding.ASCII.GetBytes(secret);
var handler = new JwtSecurityTokenHandler();
var validations = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
var claims = handler.ValidateToken(token, validations, out var tokenSecure);
return claims.Identity.Name;
}
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Email, model.UserName),
new Claim(JwtRegisteredClaimNames.NameId, model.Id.ToString()),
};
var token = new JwtSecurityToken(_config["Jwt:Issuer"],
_config["Jwt:Issuer"],
claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
Then extract content
var handler = new JwtSecurityTokenHandler();
string authHeader = Request.Headers["Authorization"];
authHeader = authHeader.Replace("Bearer ", "");
var jsonToken = handler.ReadToken(authHeader);
var tokenS = handler.ReadToken(authHeader) as JwtSecurityToken;
var id = tokenS.Claims.First(claim => claim.Type == "nameid").Value;
Using .net core jwt packages, the Claims are available:
[Route("api/[controller]")]
[ApiController]
[Authorize(Policy = "Bearer")]
public class AbstractController: ControllerBase
{
protected string UserId()
{
var principal = HttpContext.User;
if (principal?.Claims != null)
{
foreach (var claim in principal.Claims)
{
log.Debug($"CLAIM TYPE: {claim.Type}; CLAIM VALUE: {claim.Value}");
}
}
return principal?.Claims?.SingleOrDefault(p => p.Type == "username")?.Value;
}
}
I write this solution and it's work for me
protected Dictionary<string, string> GetTokenInfo(string token)
{
var TokenInfo = new Dictionary<string, string>();
var handler = new JwtSecurityTokenHandler();
var jwtSecurityToken = handler.ReadJwtToken(token);
var claims = jwtSecurityToken.Claims.ToList();
foreach (var claim in claims)
{
TokenInfo.Add(claim.Type, claim.Value);
}
return TokenInfo;
}
Extending on cooxkie answer, and dpix answer, when you are reading a jwt token (such as an access_token received from AD FS), you can merge the claims in the jwt token with the claims from "context.AuthenticationTicket.Identity" that might not have the same set of claims as the jwt token.
To Illustrate, in an Authentication Code flow using OpenID Connect,after a user is authenticated, you can handle the event SecurityTokenValidated which provides you with an authentication context, then you can use it to read the access_token as a jwt token, then you can "merge" tokens that are in the access_token with the standard list of claims received as part of the user identity:
private Task OnSecurityTokenValidated(SecurityTokenValidatedNotification<OpenIdConnectMessage,OpenIdConnectAuthenticationOptions> context)
{
//get the current user identity
ClaimsIdentity claimsIdentity = (ClaimsIdentity)context.AuthenticationTicket.Identity;
/*read access token from the current context*/
string access_token = context.ProtocolMessage.AccessToken;
JwtSecurityTokenHandler hand = new JwtSecurityTokenHandler();
//read the token as recommended by Coxkie and dpix
var tokenS = hand.ReadJwtToken(access_token);
//here, you read the claims from the access token which might have
//additional claims needed by your application
foreach (var claim in tokenS.Claims)
{
if (!claimsIdentity.HasClaim(claim.Type, claim.Value))
claimsIdentity.AddClaim(claim);
}
return Task.FromResult(0);
}
Use this:
public static string Get_Payload_JWTToken(string token)
{
var handler = new JwtSecurityTokenHandler();
var DecodedJWT = handler.ReadJwtToken(token);
string payload = DecodedJWT.EncodedPayload; // Gives Payload
return Encoding.UTF8.GetString(FromBase64Url(payload));
}
static byte[] FromBase64Url(string base64Url)
{
string padded = base64Url.Length % 4 == 0
? base64Url : base64Url + "====".Substring(base64Url.Length % 4);
string base64 = padded.Replace("_", "/").Replace("-", "+");
return Convert.FromBase64String(base64);
}
Though this answer is not answering the original question but its a really very useful feature for C# developers, so adding it as the answer.
Visual Studio 2022 has added a feature to decode the value of a token at runtime.
You can check the feature in Visual Studio 2022 preview (version 17.5.0 preview 2.0)
Mouse over the variable containing the JWT and then select the string manipulation as JWT Decode, and you can see the token value.
I have a JWT token created using PHP that I need to then use in a .NET app (framework version 4.5.1). The token is generated in PHP using the following code (relies on https://github.com/lcobucci/jwt library):
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Hmac\Sha256;
$tokenBuilder = new Builder();
$tokenSigner = new Sha256();
$token = (string)$tokenBuilder
->setIssuer('localhost:8081')
->setAudience('myaudience')
->setIssuedAt(time())
->setExpiration(time() + 86400)
->sign($tokenSigner, '710VWV0zby')
->getToken();
return $token;
I've been able to read the token fine in C#, but am struggling mightily to figure out how to validate and verify the token signature.
TokenValidationParameters validationParameters = new TokenValidationParameters
{
ValidateIssuer = true
};
var tokenHandler = new JwtSecurityTokenHandler();
// THIS IS TO TEST IF TOKEN CAN BE READ
/*var jwtToken = tokenHandler.ReadJwtToken(token);
HttpContext.Current.Response.Write(jwtToken.Issuer);*/
SecurityToken validatedToken = null;
try
{
tokenHandler.ValidateToken(token, validationParameters, out validatedToken);
}
catch (Exception)
{
HttpContext.Current.Response.Write("Invalid! :(");
}
if (validatedToken != null) {
HttpContext.Current.Response.Write("Valid! :)");
}
Obviously, my code can't verify any signature, given that there's not even any mention of a SHA-256 key anywhere. I'm assuming I need to include that somehow in the TokenValidationParameters there's a property I need to set, and I'm guessing SigningToken would be the one, but I don't really know where to start to specify an HMAC SHA 256 key.
You need somehow export certificate that was using for token creation. It can be file in .pem format for example. After that create crypto provider using data from that certificate
public static RSACryptoServiceProvider CreateRsaCryptoProviderFromX509Certificate()
{
byte[] certData = Convert.FromBase64String(_CERTIFICATE);
X509Certificate2 x509Cert = new X509Certificate2();
x509Cert.Import(certData);
var x509PublicKeyXml = x509Cert.PublicKey.Key.ToXmlString(false);
RSACryptoServiceProvider RsaProvider = new RSACryptoServiceProvider();
RsaProvider.FromXmlString(x509PublicKeyXml);
return RsaProvider;
}
where _CERTIFICATE store data from .pem file without leading coments
string _CERTIFICATE = #" DD5NYXRyaXg0Ml9mNThlMzdkLWU2ZjktNGU0Yi05MzVlLTNhMDFi
NzU2N2I5YjAeFw0xNjEyMzExNTE1MjNaFw00MjAxMDcxNTE1MjNaMEkxRzBFBgNVBAMMPk1hdHJp
eDQyX0FDU19SZWx5aW5nUGFydHlfZmY1OGUzN2QtZTZmOS00ZTRiLTkzNWUtM2EwMWI3NTY3Yjli
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAho5G6pY9QJs/945aQ1w8oiF/17ZNGsNY
ul5G/+TprN7KfgzT9u+A588f4Z4B8z5QJlwIUeH33iuRcV0AIHd9MnEKR56IdOLLlNWNPvRAG5FJ
Wt4XPlaG+bE/oyuqxqpQM1KJ0iN74K/WLXM8ZdQlq7gTgtLS+icZH3i2arC8rdobh3zRk1wbUVXn
kjR4CASy+07LZwbVVp2g3pOsuy5AWBURIynQ7z3zj+u7NMF42htLOEzISl3Qb3BMOoXFMm93UGwp
B/Ae+zpWFWeh6190ipcUMXoAOfdh9VZUZX9C7OI/3plOiwKUvwfBQyLR8C/4uiTcCTp1i8fS0bta
jkPhdQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA3fmwEgej+BhB7dkw+0TWEDiIC9cXR4uW7kElM
7+L7ARmUYVpAx05Z8oarsR0zm1u3ZYR00y3eLhw5RcXN6hC5jb5HYSQZERdqlzvS6bU6xJ57H7tC
KuPADkYmuPnRM/cdMKPeSG3ZHnHcTgJx62hFloPWbGPr9VLVp4R4coUgtuZMtlFvXamjpCNYSpob
N9wzk36r/4c+Nd/n+4DwqIaVzgEXHXkOUtOZhTYh7SG5WJVUSep5cIq3SBGzLn8oXCjiqn72zJ7C
vn5/ekaC1nzMDMcga5qWQNdLd/rXt65ZMbB/JhM+Ee9TIvmrrDXlvRh2cv7GtoTtPYEbIdVvrF+W";
for validation create token handler, validation parameters and validate token with cryptoprovider
public static bool ValidateJwt(string jwt)
{
JwtSecurityTokenHandler securityTokenHandler = new JwtSecurityTokenHandler();
RSACryptoServiceProvider rsa = CreateRsaCryptoProviderFromX509Certificate();
TokenValidationParameters validationParameters = new TokenValidationParameters()
{
ValidAudience ="urn:6c23aaa7-6da8-4941-98b0-62f63cd146",
ValidIssuer = "https://accounts.issuer.com",
IssuerSigningKey = new RsaSecurityKey(rsa)
};
SecurityToken token;
ClaimsPrincipal claimsPrincipal = securityTokenHandler.ValidateToken(jwt, validationParameters, out token);
return claimsPrincipal.IsInRole("Admin");
}
I'm implmenting my own oauth authtentication system and want to use JWT tokens.
The ms implementation is a little confusing. I see the following hashing algorithum all over the net
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
string symmetricKeyAsBase64 = ConfigurationManager.AppSettings["as:AudienceSecret"];
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
var signingKey = new HmacSigningCredentials(keyByteArray);
var issued = data.Properties.IssuedUtc;
var expires = data.Properties.ExpiresUtc;
var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.WriteToken(token);
return jwt;
}
but can't seem to find out how I can then extract the user info out of the token
what you are showing is a JWT protect implementation that does not come out of the box. This is for serializing & encrypting tokens not the oposite. If you have already managed that part (that's the difficult one) then reading the user claims out of a JWT token should be easy. This is usually done by the Microsoft.Owin.Security.Jwt middleware
var issuer = "http://myidentityserverurl.com"
var audience = ConfigurationManager.AppSettings["as:AudienceId"];
var symmetricKey = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["as:AudienceSecret"]);
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions {
AuthenticationMode = AuthenticationMode.Active,
AuthenticationType = "JWT",
AllowedAudiences = new string[] { audience } ,
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, symmetricKey)
}
});
Check out this github repo and the accompanying article here for a complete example end to end.