Existe-t-il un exemple de jeton Web JSON (JWT) dans C #?

J’ai l’impression de prendre des pilules folles ici. Habituellement, il y a toujours un million de bibliothèques et des échantillons flottant sur le Web pour une tâche donnée. J’essaie d’implémenter l’authentification avec un “compte de service” Google en utilisant les jetons Web JSON (JWT), comme décrit ici .

Cependant, il n’y a que des bibliothèques clientes en PHP, Python et Java. Même en recherchant des exemples de JWT en dehors de l’authentification de Google, il n’y a que des grillons et des brouillons sur le concept JWT. Est-ce vraiment si nouveau et peut-être un système propriétaire de Google?

L’échantillon java qui est le plus proche de ce que j’ai pu interpréter est plutôt intense et intimidant. Il doit y avoir quelque chose en C # par lequel je pourrais au moins commencer. Toute aide avec ce serait génial!

Merci tout le monde. J’ai trouvé une implémentation de base d’un Json Web Token et je l’ai développée avec l’arôme de Google. Je ne l’ai toujours pas complètement réglé, mais il y en a 97%. Ce projet a perdu de sa vigueur, alors j’espère que cela aidera quelqu’un d’autre à prendre une bonne longueur d’avance:

Note: Les modifications que j’ai apscopes à l’implémentation de base (je ne me souviens plus où je l’ai trouvé) sont les suivantes:

  1. HS256 modifié -> RS256
  2. Échange de l’ordre JWT et alg dans l’en-tête. Pas sûr de savoir qui a eu tort, Google ou la spécification, mais google le prend comme il est ci-dessous selon leurs documents.
public enum JwtHashAlgorithm { RS256, HS384, HS512 } public class JsonWebToken { private static Dictionary> HashAlgorithms; static JsonWebToken() { HashAlgorithms = new Dictionary> { { JwtHashAlgorithm.RS256, (key, value) => { using (var sha = new HMACSHA256(key)) { return sha.ComputeHash(value); } } }, { JwtHashAlgorithm.HS384, (key, value) => { using (var sha = new HMACSHA384(key)) { return sha.ComputeHash(value); } } }, { JwtHashAlgorithm.HS512, (key, value) => { using (var sha = new HMACSHA512(key)) { return sha.ComputeHash(value); } } } }; } public static ssortingng Encode(object payload, ssortingng key, JwtHashAlgorithm algorithm) { return Encode(payload, Encoding.UTF8.GetBytes(key), algorithm); } public static ssortingng Encode(object payload, byte[] keyBytes, JwtHashAlgorithm algorithm) { var segments = new List(); var header = new { alg = algorithm.ToSsortingng(), typ = "JWT" }; byte[] headerBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(header, Formatting.None)); byte[] payloadBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(payload, Formatting.None)); //byte[] payloadBytes = Encoding.UTF8.GetBytes(@"{"iss":"761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com","scope":"https://www.googleapis.com/auth/prediction","aud":"https://accounts.google.com/o/oauth2/token","exp":1328554385,"iat":1328550785}"); segments.Add(Base64UrlEncode(headerBytes)); segments.Add(Base64UrlEncode(payloadBytes)); var ssortingngToSign = ssortingng.Join(".", segments.ToArray()); var bytesToSign = Encoding.UTF8.GetBytes(ssortingngToSign); byte[] signature = HashAlgorithms[algorithm](keyBytes, bytesToSign); segments.Add(Base64UrlEncode(signature)); return ssortingng.Join(".", segments.ToArray()); } public static ssortingng Decode(ssortingng token, ssortingng key) { return Decode(token, key, true); } public static ssortingng Decode(ssortingng token, ssortingng key, bool verify) { var parts = token.Split('.'); var header = parts[0]; var payload = parts[1]; byte[] crypto = Base64UrlDecode(parts[2]); var headerJson = Encoding.UTF8.GetSsortingng(Base64UrlDecode(header)); var headerData = JObject.Parse(headerJson); var payloadJson = Encoding.UTF8.GetSsortingng(Base64UrlDecode(payload)); var payloadData = JObject.Parse(payloadJson); if (verify) { var bytesToSign = Encoding.UTF8.GetBytes(ssortingng.Concat(header, ".", payload)); var keyBytes = Encoding.UTF8.GetBytes(key); var algorithm = (ssortingng)headerData["alg"]; var signature = HashAlgorithms[GetHashAlgorithm(algorithm)](keyBytes, bytesToSign); var decodedCrypto = Convert.ToBase64Ssortingng(crypto); var decodedSignature = Convert.ToBase64Ssortingng(signature); if (decodedCrypto != decodedSignature) { throw new ApplicationException(ssortingng.Format("Invalid signature. Expected {0} got {1}", decodedCrypto, decodedSignature)); } } return payloadData.ToSsortingng(); } private static JwtHashAlgorithm GetHashAlgorithm(ssortingng algorithm) { switch (algorithm) { case "RS256": return JwtHashAlgorithm.RS256; case "HS384": return JwtHashAlgorithm.HS384; case "HS512": return JwtHashAlgorithm.HS512; default: throw new InvalidOperationException("Algorithm not supported."); } } // from JWT spec private static ssortingng Base64UrlEncode(byte[] input) { var output = Convert.ToBase64Ssortingng(input); output = output.Split('=')[0]; // Remove any trailing '='s output = output.Replace('+', '-'); // 62nd char of encoding output = output.Replace('/', '_'); // 63rd char of encoding return output; } // from JWT spec private static byte[] Base64UrlDecode(ssortingng input) { var output = input; output = output.Replace('-', '+'); // 62nd char of encoding output = output.Replace('_', '/'); // 63rd char of encoding switch (output.Length % 4) // Pad with trailing '='s { case 0: break; // No pad chars in this case case 2: output += "=="; break; // Two pad chars case 3: output += "="; break; // One pad char default: throw new System.Exception("Illegal base64url ssortingng!"); } var converted = Convert.FromBase64Ssortingng(output); // Standard base64 decoder return converted; } } 

Et puis ma classe google spécifique JWT:

 public class GoogleJsonWebToken { public static ssortingng Encode(ssortingng email, ssortingng certificateeFilePath) { var utc0 = new DateTime(1970,1,1,0,0,0,0, DateTimeKind.Utc); var issueTime = DateTime.Now; var iat = (int)issueTime.Subtract(utc0).TotalSeconds; var exp = (int)issueTime.AddMinutes(55).Subtract(utc0).TotalSeconds; // Expiration time is up to 1 hour, but lets play on safe side var payload = new { iss = email, scope = "https://www.googleapis.com/auth/gan.readonly", aud = "https://accounts.google.com/o/oauth2/token", exp = exp, iat = iat }; var certificatee = new X509Certificate2(certificateeFilePath, "notasecret"); var privateKey = certificatee.Export(X509ContentType.Cert); return JsonWebToken.Encode(payload, privateKey, JwtHashAlgorithm.RS256); } } 

Après que tous ces mois se soient écoulés après la question initiale, il convient maintenant de souligner que Microsoft a conçu sa propre solution. Voir http://blogs.msdn.com/b/vbertocci/archive/2012/11/20/introducing-the-developer-preview-of-the-json-web-token-handler-for-the-microsoft-net -framework-4-5.aspx pour plus de détails.

Je ne l’ai jamais utilisé mais il existe une implémentation JWT sur NuGet.

Package: https://nuget.org/packages/JWT

Source: https://github.com/johnsheehan/jwt

Compatible avec .NET 4.0: https://www.nuget.org/packages/jose-jwt/

Vous pouvez aussi aller ici: https://jwt.io/ et cliquer sur “bibliothèques”.

Voici un exemple de travail:

http://zavitax.wordpress.com/2012/12/17/logging-in-with-google-service-account-in-c-jwt/

Il a fallu un certain temps pour collecter les pièces éparpillées sur le web, les documents sont plutôt incomplets …

Ceci est ma mise en œuvre de la validation (Google) JWT dans .NET. Il est basé sur d’autres implémentations sur les emstackments Stack Overflow et GitHub.

 using Microsoft.IdentityModel.Tokens; using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Net.Http; using System.Security.Claims; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading.Tasks; namespace QuapiNet.Service { public class JwtTokenValidation { public async Task> FetchGoogleCertificates() { using (var http = new HttpClient()) { var response = await http.GetAsync("https://www.googleapis.com/oauth2/v1/certs"); var dictionary = await response.Content.ReadAsAsync>(); return dictionary.ToDictionary(x => x.Key, x => new X509Certificate2(Encoding.UTF8.GetBytes(x.Value))); } } private ssortingng CLIENT_ID = "xxx.apps.googleusercontent.com"; public async Task ValidateToken(ssortingng idToken) { var certificatees = await this.FetchGoogleCertificates(); TokenValidationParameters tvp = new TokenValidationParameters() { ValidateActor = false, // check the profile ID ValidateAudience = true, // check the client ID ValidAudience = CLIENT_ID, ValidateIssuer = true, // check token came from Google ValidIssuers = new List { "accounts.google.com", "https://accounts.google.com" }, ValidateIssuerSigningKey = true, RequireSignedTokens = true, IssuerSigningKeys = certificatees.Values.Select(x => new X509SecurityKey(x)), IssuerSigningKeyResolver = (token, securityToken, kid, validationParameters) => { return certificatees .Where(x => x.Key.ToUpper() == kid.ToUpper()) .Select(x => new X509SecurityKey(x.Value)); }, ValidateLifetime = true, RequireExpirationTime = true, ClockSkew = TimeSpan.FromHours(13) }; JwtSecurityTokenHandler jsth = new JwtSecurityTokenHandler(); SecurityToken validatedToken; ClaimsPrincipal cp = jsth.ValidateToken(idToken, tvp, out validatedToken); return cp; } } } 

Notez que, pour l’utiliser, vous devez append une référence au package System.Net.Http.Formatting.Extension . Sans cela, le compilateur ne reconnaîtra pas la méthode ReadAsAsync<> .

Jetez un coup d’œil à la bibliothèque client Google pour .NET .