24 October 2013 JWT, OAuth, Security Robert Muehsig

Wer sich mit Authentifizierung im Web befasst wird früher oder später auf den Begriff JSON Web Token treffen (JWT).

Was ist ein JWT?
Vermutlich nutze ich nicht die Security-Fach-Termini, aber seis drum: JWTs werden eingesetzt um Claims zwischen zwei Systemen auszutauschen. Beispiel: Über eine App möchte man sich an einem Dienst (Facebook, Twitter, etc.) einloggen und seine Daten sehen. Als Rückantwort von dem Dienst kommt ein JWT welches die Claims des authentifizierten und authorisierten Nutzers enthält. Ein Claim ist im Grunde nur ein Key/Value Pair wo man so ziemlich alles unterbringen kann was man sich vorstellen kann.

Die volle Spezifikation enthält dazu wesentlich mehr Informationen.

Wozu eigene JWTs?

Wer bereits sein eigenes Authentifizierungssystem am laufen hat und nun ebenfalls eine OAuth Schnittstelle anbieten möchte (für Apps), der wird früher oder später sich damit befassen wie sich Clients am System anmelden können. Das JWT kann dann als Transportmedium dienen.

Eigene JWTs erstellen & validieren

Mit dem .NET Framework 4.5 und JSON Web Token Handler NuGet Package kann man sowohl Tokens von anderen Diensten validieren oder auch selber welche erstellen. Der Code stammt zu 99% aus diesem Blog, welcher noch weitere Security und HTTP Themen behandelt.

   1: // Code source is from this awesome blog: 
   2: // http://pfelix.wordpress.com/2012/11/27/json-web-tokens-and-the-new-jwtsecuritytokenhandler-class/
   3: class Program
   4: {
   5:     static void Main(string[] args)
   6:     {
   7:         var securityKey = GetBytes("ThisIsAnImportantStringAndIHaveNoIdeaIfThisIsVerySecureOrNot!");
   8:  
   9:         var tokenHandler = new JwtSecurityTokenHandler();
  10:  
  11:         // Token Creation
  12:         var now = DateTime.UtcNow;
  13:         var tokenDescriptor = new SecurityTokenDescriptor
  14:         {
  15:             Subject = new ClaimsIdentity(new Claim[]
  16:                     {
  17:                         new Claim(ClaimTypes.Name, "Pedro"),
  18:                         new Claim(ClaimTypes.Role, "Author"), 
  19:                     }),
  20:             TokenIssuerName = "self",
  21:             AppliesToAddress = "http://www.example.com",
  22:             Lifetime = new Lifetime(now, now.AddMinutes(2)),
  23:             SigningCredentials = new SigningCredentials(
  24:                 new InMemorySymmetricSecurityKey(securityKey),
  25:                 "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
  26:                 "http://www.w3.org/2001/04/xmlenc#sha256"),
  27:         };
  28:         var token = tokenHandler.CreateToken(tokenDescriptor);
  29:  
  30:         // Generate Token and return string
  31:         var tokenString = tokenHandler.WriteToken(token);
  32:         Console.WriteLine(tokenString);
  33:         
  34:         // Token Validation
  35:         var validationParameters = new TokenValidationParameters()
  36:         {
  37:             AllowedAudience = "http://www.example.com",
  38:             SigningToken = new BinarySecretSecurityToken(securityKey),
  39:             ValidIssuer = "self"
  40:         };
  41:  
  42:         // from Token to ClaimsPrincipal - easy!
  43:         var principal = tokenHandler.ValidateToken(tokenString, validationParameters);
  44:  
  45:         Console.WriteLine(principal.Claims.Single(x => x.Type == ClaimTypes.Name).Value);
  46:  
  47:         Console.ReadLine();
  48:     }
  49:  
  50:     static byte[] GetBytes(string str)
  51:     {
  52:         byte[] bytes = new byte[str.Length * sizeof(char)];
  53:         System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
  54:         return bytes;
  55:  
  56:     }
  57: }

Erklärung

Im ersten Schritt wird ein SecurityKey erzeugt – dieser wird für den TokenHandler benötigt. In diesem Fall ist es ein symetrischer Schlüssel, d.h. beide Partein benötigen den vollen Schlüssel. Ein JWT kann man mit den unterschiedlichsten Verfahren sichern (Zertifikate etc.).

Über den Token Handler gibt man einen ClaimsPrincipal rein – diese Claims möchten wir am Ende wieder lesen können.

Der Token wird erstellt mit allen Parametern und mit denselben Parametern und dem Schlüssel kann das Token wieder lesbar gemacht werden.

Diesen Token könnte man auch zwischen Server Applikationen austauschen lassen oder zwischen App und Service.

Das Bild zeigt die Ausgabe des Programms: Den Token und am Ende den “Name”-Claim.

image

Der Code ist natürlich auch auf GitHub verfügbar.


Written by Robert Muehsig

Software Developer - from Saxony, Germany - working on primedocs.io. Microsoft MVP & Web Geek.
Other Projects: KnowYourStack.com | ExpensiveMeeting | EinKofferVollerReisen.de