JWT (JavaScript Web Tokens)
JavaScript Web Tokens bzw. die Abkürzung JWT sind eine in sich geschlossene Technik um Informationen sicher zu übertragen. Definiert ist das Ganze in einem offenen Standard (RFC 7519).
Dadurch, dass der JWT signiert ist (HMAC oder RSA), wird gewährleistet, dass die darin enthaltenen Informationen nicht verändert wurden.
Der primäre Einsatzzweck ist Autorisierung. Sobald sich ein Benutzer angemeldet hat, wird ein JWT erstellt, der bei allen zukünftigen Anfragen mit gesendet werden muss (bspw. als Authorization-Header bei REST-Requests).
JWT-Struktur
Ein JWT besteht aus drei Elementen, die jeweils durch einen Punkt getrennt sind.
Header
Der Header besteht normalerweise aus zwei Teilen: dem Typ (= JWT) und verwendeten Algorithmus für die Signatur (bspw. HS256).
Der Wert ist ein base64 enkodiertes JSON-Objekt.
Payload
Der Payload enthält sogenannte Claims, die Informationen zum Benutzer enthalten (bspw. den Benutzername, E-Mail, ID des Benutzers, …).
Es gibt drei Gruppierungen von Claims:
Registered Claims: Dies sind Standard-Claims, die nicht verpflichtend sind. Dennoch wird empfohlen diese zu setzen. Dabei handelt es sich um den Ersteller (iss; issuer), Ablaufdatum (exp; expiration time) oder Betreff (sub; subject).
Public Claims: Diese sind solche, die in der IANA JWT Registry definiert wurden oder eine URI, die kollisionsresistent ist.
Private Claims: Hierbei handelt es sich um eigen erstellte Claims.
Der Wert ist ein base64 enkodiertes JSON-Objekt.
Signature
Hierfür wird der base64 enkodierte Header und Payload mit einem Schlüssel und dem im Header angegebenen Signatur-Algorithmus signiert.
Quelle: https://jwt.io/introduction/.
Beispiel in C#
Für dieses Beispiel wird folgendes NuGet-Paket benötigt:
- System.IdentityModel.Tokens.Jwt
Token-Handler und Secret erstellen
Im ersten Schritt wird ein Token-Handler und ein Secret erstellt. Diese Komponenten werden für das Erstellen, auslesen und Verifizieren der Signatur benötigt.
var tokenHandler = new JwtSecurityTokenHandler();
var secret = "MEIN_GEHEIMES_SECRET";
var secretKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secret ));
Token erstellen
Als nächster Schritt kann der Token erstellt werden.
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim("uid", "150"),
}),
Expires = DateTime.UtcNow.AddDays(7),
Issuer = "http://www.issuer.at",
Audience = "http://www.audience.at",
SigningCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
Dieser kann nach erfolgreichem Login der anderen Partei übergeben werden. Bei jedem zukünftigen Aufruf eines Endpunkts muss dieser ebenfalls übergeben werden.
Token validieren
Beim Erhalt eines Tokens muss dieser unbedingt geprüft werden, ob dieser nicht verändert wurde. Die „ValidateToken“-Methode wirft eine Exception, wenn die Signatur nicht passt.
isTokenValid = false;
try
{
tokenHandler.ValidateToken(tokenString, new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidateAudience = true,
ValidIssuer = "http://www.issuer.at",
ValidAudience = "http://www.audience.at",
IssuerSigningKey = secretKey
}, out SecurityToken validatedToken);
isTokenValid = true;
}
catch
{
}
Claims aus Token lesen
Abschließend noch die Information, wie Claims aus einem Token ausgelesen werden können.
var securityToken = tokenHandler.ReadToken(tokenString) as JwtSecurityToken;
var stringClaimValue = securityToken.Claims.First(claim => claim.Type == "uid").Value;