Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any JSON Web Token (JWT) example in C#?

I feel like I'm taking crazy pills here. Usually there's always a million library and samples floating around the web for any given task. I'm trying to implement authentication with a Google "Service Account" by use of JSON Web Tokens (JWT) as described here.

However there is only client libraries in PHP, Python, and Java. Even searching for JWT examples outside of Google's authentication, there is only crickets and drafts on the JWT concept. Is this really so new and possibly a Google proprietary system?

The java sample which is the closest I could manage to interpret looks pretty intensive and intimidating. There's got to be something out there in C# that I could at least start with. Any help with this would be great!

like image 906
Levitikon Avatar asked Apr 07 '12 14:04

Levitikon


People also ask

What is JWT token C#?

JWT (JSON web token) has become more and more popular in web development. It is an open standard which allows transmitting data between parties as a JSON object in a secure and compact way. The data transmitting using JWT between parties are digitally signed so that it can be easily verified and trusted.

Why JWT is called JSON Web Token?

JSON web token (JWT), pronounced "jot", is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. Again, JWT is a standard, meaning that all JWTs are tokens, but not all tokens are JWTs.

Is JWT a JSON?

JWT, or JSON Web Token, is an open standard used to share security information between two parties — a client and a server. Each JWT contains encoded JSON objects, including a set of claims. JWTs are signed using a cryptographic algorithm to ensure that the claims cannot be altered after the token is issued.


2 Answers

Thanks everyone. I found a base implementation of a Json Web Token and expanded on it with the Google flavor. I still haven't gotten it completely worked out but it's 97% there. This project lost it's steam, so hopefully this will help someone else get a good head-start:

Note: Changes I made to the base implementation (Can't remember where I found it,) are:

  1. Changed HS256 -> RS256
  2. Swapped the JWT and alg order in the header. Not sure who got it wrong, Google or the spec, but google takes it the way It is below according to their docs.
public enum JwtHashAlgorithm {     RS256,     HS384,     HS512 }  public class JsonWebToken {     private static Dictionary<JwtHashAlgorithm, Func<byte[], byte[], byte[]>> HashAlgorithms;      static JsonWebToken()     {         HashAlgorithms = new Dictionary<JwtHashAlgorithm, Func<byte[], byte[], byte[]>>             {                 { 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 string Encode(object payload, string key, JwtHashAlgorithm algorithm)     {         return Encode(payload, Encoding.UTF8.GetBytes(key), algorithm);     }      public static string Encode(object payload, byte[] keyBytes, JwtHashAlgorithm algorithm)     {         var segments = new List<string>();         var header = new { alg = algorithm.ToString(), 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 stringToSign = string.Join(".", segments.ToArray());          var bytesToSign = Encoding.UTF8.GetBytes(stringToSign);          byte[] signature = HashAlgorithms[algorithm](keyBytes, bytesToSign);         segments.Add(Base64UrlEncode(signature));          return string.Join(".", segments.ToArray());     }      public static string Decode(string token, string key)     {         return Decode(token, key, true);     }      public static string Decode(string token, string key, bool verify)     {         var parts = token.Split('.');         var header = parts[0];         var payload = parts[1];         byte[] crypto = Base64UrlDecode(parts[2]);          var headerJson = Encoding.UTF8.GetString(Base64UrlDecode(header));         var headerData = JObject.Parse(headerJson);         var payloadJson = Encoding.UTF8.GetString(Base64UrlDecode(payload));         var payloadData = JObject.Parse(payloadJson);          if (verify)         {             var bytesToSign = Encoding.UTF8.GetBytes(string.Concat(header, ".", payload));             var keyBytes = Encoding.UTF8.GetBytes(key);             var algorithm = (string)headerData["alg"];              var signature = HashAlgorithms[GetHashAlgorithm(algorithm)](keyBytes, bytesToSign);             var decodedCrypto = Convert.ToBase64String(crypto);             var decodedSignature = Convert.ToBase64String(signature);              if (decodedCrypto != decodedSignature)             {                 throw new ApplicationException(string.Format("Invalid signature. Expected {0} got {1}", decodedCrypto, decodedSignature));             }         }          return payloadData.ToString();     }      private static JwtHashAlgorithm GetHashAlgorithm(string 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 string Base64UrlEncode(byte[] input)     {         var output = Convert.ToBase64String(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(string 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 string!");         }         var converted = Convert.FromBase64String(output); // Standard base64 decoder         return converted;     } } 

And then my google specific JWT class:

public class GoogleJsonWebToken {     public static string Encode(string email, string certificateFilePath)     {         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 certificate = new X509Certificate2(certificateFilePath, "notasecret");          var privateKey = certificate.Export(X509ContentType.Cert);          return JsonWebToken.Encode(payload, privateKey, JwtHashAlgorithm.RS256);     } } 
like image 66
Levitikon Avatar answered Sep 22 '22 19:09

Levitikon


After all these months have passed after the original question, it's now worth pointing out that Microsoft has devised a solution of their own. See 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 for details.

like image 43
Jouni Heikniemi Avatar answered Sep 19 '22 19:09

Jouni Heikniemi