Skip to content

Commit

Permalink
Adding JwtBuilder (#140)
Browse files Browse the repository at this point in the history
* Adding JwtBuilder and its dependencies/internals
* Updating, refactoring tests
* Bumping nuget version to 3.2.0-beta
* Updating xml doc
  • Loading branch information
paule96 authored and abatishchev committed Dec 13, 2017
1 parent de16d6e commit 3e62ccd
Show file tree
Hide file tree
Showing 32 changed files with 1,119 additions and 185 deletions.
94 changes: 70 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,20 @@ This library supports generating and decoding [JSON Web Tokens](http://tools.iet
## Installation
Package is avaliable via [NuGet](https://nuget.org/packages/JWT). Or you can download and compile it yourself.

## Supported .NET Framework versions
As of version 2.0, the lowest Supported version is 4.6.1
## Supported .NET Framework versions:
- .NET 4.6.0
- .NET Standard 1.3

## Usage
### Creating (Encoding) Tokens
### Creating (encoding) token

```csharp
var payload = new Dictionary<string, object>
{
{ "claim1", 0 },
{ "claim2", "claim2-value" }
};
var secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";
const string secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";

IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
IJsonSerializer serializer = new JsonNetSerializer();
Expand All @@ -30,15 +31,28 @@ var token = encoder.Encode(payload, secret);
Console.WriteLine(token);
```

Output will be:
## Or using the fluent builder API

```csharp
var token = new JwtBuilder().
.SetAlgorithm(new HMACSHA256Algorithm())
.SetSecret(secret)
.AddClaim(ClaimName.ExpirationTime, DateTime.UtcNow.AddHours(1))
.Build();

Console.WriteLine(token);
```

The output would be:

>eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjbGFpbTEiOjAsImNsYWltMiI6ImNsYWltMi12YWx1ZSJ9.8pwBI_HtXqI3UgQHQ_rDRnSQRxFL1SR8fbQoS-5kM5s
### Parsing (Decoding) and Verifying Tokens
### Parsing (decoding) and verifying token

```csharp
var token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjbGFpbTEiOjAsImNsYWltMiI6ImNsYWltMi12YWx1ZSJ9.8pwBI_HtXqI3UgQHQ_rDRnSQRxFL1SR8fbQoS-5kM5s";
var secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";
const string token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjbGFpbTEiOjAsImNsYWltMiI6ImNsYWltMi12YWx1ZSJ9.8pwBI_HtXqI3UgQHQ_rDRnSQRxFL1SR8fbQoS-5kM5s";
const string secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";

try
{
IJsonSerializer serializer = new JsonNetSerializer();
Expand All @@ -60,62 +74,94 @@ catch (SignatureVerificationException)
}
```

Output will be:
## Or using the fluent builder API

```csharp
try
{
var json = new JwtBuilder()
.SetSecret(secret)
.MustVerifySignature()
.Decode(token);
Console.WriteLine(json);
}
catch (TokenExpiredException)
{
Console.WriteLine("Token has expired");
}
catch (SignatureVerificationException)
{
Console.WriteLine("Token has invalid signature");
}
```

The output would be:

>{ "claim1": 0, "claim2": "claim2-value" }
You can also deserialize the JSON payload directly to a .NET type with `DecodeToObject<T>`:
You can also deserialize the JSON payload directly to a .NET type:

```csharp
var payload = decoder.DecodeToObject<IDictionary<string, object>>(token, secret);
Console.WriteLine(payload["claim2"]);
```

## Or using the fluent builder API

```csharp
var payload = new JwtBuilder()
.SetSecret(secret)
.MustVerifySignature()
.Decode<IDictionary<string, object>>(token);
Console.WriteLine(payload["claim2"]);
```

Output will be:
The output would be:

>claim2-value
#### exp claim
#### Set and validate token expiration

As described in the [JWT RFC](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32#section-4.1.4), the `exp` "claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing." If an `exp` claim is present and is prior to the current time the token will fail verification. The exp (expiry) value must be specified as the number of seconds since 1/1/1970 UTC.

```csharp
IDateTimeProvider provider = new UtcDateTimeProvider();
var now = provider.GetNow();

var unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); // or use JwtValidator.UnixEpoch
var unixEpoch = JwtValidator.UnixEpoch; // 1970-01-01 00:00:00 UTC
var secondsSinceEpoch = Math.Round((now - unixEpoch).TotalSeconds);

var payload = new Dictionary<string, object>
{
{ "exp", secondsSinceEpoch }
};
var secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";
cosnt string secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";
var token = encoder.Encode(payload, secret);

var json = decoder.Decode(token, secret); // TokenExpiredException
var json = decoder.Decode(token, secret); // throws TokenExpiredException
```

### Custom JSON serializer

By default JSON serialization is done by JsonNetSerializer implemented using [Json.Net](https://www.json.net). To configure a different one first implement the `IJsonSerializer` interface:
By default JSON serialization is performed by JsonNetSerializer implemented using [Json.Net](https://www.json.net). To use a different one, implement the `IJsonSerializer` interface:

```csharp
public class CustomJsonSerializer : IJsonSerializer
{
public string Serialize(object obj)
{
// Implement using favorite JSON Serializer
// Implement using favorite JSON serializer
}

public T Deserialize<T>(string json)
{
// Implement using favorite JSON Serializer
// Implement using favorite JSON serializer
}
}
```

And then pass this serializer as a dependency to JwtEncoder constructor:

```csharp
IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
IJsonSerializer serializer = new CustomJsonSerializer();
Expand All @@ -130,20 +176,20 @@ As mentioned above, the default JSON serialization is done by `JsonNetSerializer
```csharp
JsonSerializer customJsonSerializer = new JsonSerializer
{
// All json keys start with lowercase characters instead of the exact casing of the model/property. e.g. fullName
// All json keys start with lowercase characters instead of the exact casing of the model/property, e.g. fullName
ContractResolver = new CamelCasePropertyNamesContractResolver(),

// Nice and easy to read, but you can also do Formatting.None to reduce the payload size (by hardly anything...)
// Nice and easy to read, but you can also use Formatting.None to reduce the payload size
Formatting = Formatting.Indented,

// The best date/time format/standard.
// The most appropriate datetime format.
DateFormatHandling = DateFormatHandling.IsoDateFormat,

// Don't add key/values when the value is null.
// Don't add keys/values when the value is null.
NullValueHandling = NullValueHandling.Ignore,

   // Use the enum string-value, not the implicit int value, e.g. "oolor" : "red"
   // Use the enum string value, not the implicit int value, e.g. "red" for enum Color { Red }
Converters.Add(new StringEnumConverter())
};
IJsonSerializer serializer = new JsonNetSerializer(customJsonSerializer);
```
```
142 changes: 142 additions & 0 deletions src/JWT/Builder/ClaimName.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
using System.ComponentModel;

namespace JWT.Builder
{
/// <summary>
/// All public claims of a JWT specified by IANA, see https://www.iana.org/assignments/jwt/jwt.xhtml
/// </summary>
/// <remarks>
/// Latest update: 31.10.2017
/// </remarks>
public enum ClaimName
{
[Description("iss")]
Issuer,

[Description("sub")]
Subject,

[Description("aud")]
Audience,

[Description("exp")]
ExpirationTime,

[Description("nbf")]
NotBefore,

[Description("iat")]
IssuedAt,

[Description("jti")]
JwtId,

[Description("name")]
FullName,

[Description("given_name")]
GivenName,

[Description("family_name")]
FamilyName,

[Description("middle_name")]
MiddleName,

[Description("nickname")]
CasualName,

[Description("preferred_username")]
PreferredUsername,

[Description("profile")]
ProfilePageUrl,

[Description("picture")]
ProfilePictureUrl,

[Description("website")]
Website,

[Description("email")]
PreferredEmail,

[Description("email_verified")]
VerifiedEmail,

[Description("gender")]
Gender,

[Description("birthdate")]
Birthday,

[Description("zoneinfo")]
TimeZone,

[Description("locale")]
Locale,

[Description("phone_number")]
PreferredPhoneNumber,

[Description("phone_number_verified")]
VerifiedPhoneNumber,

[Description("address")]
Address,

[Description("update_at")]
UpdatedAt,

[Description("azp")]
AuthorizedParty,

[Description("nonce")]
Nonce,

[Description("auth_time")]
AuthenticationTime,

[Description("at_hash")]
AccessTokenHash,

[Description("c_hash")]
CodeHashValue,

[Description("acr")]
Acr,

[Description("amr")]
Amr,

[Description("sub_jwk")]
PublicKey,

[Description("cnf")]
Confirmation,

[Description("sip_from_tag")]
SipFromTag,

[Description("sip_date")]
SipDate,

[Description("sip_callid")]
SipCallId,

[Description("sip_cseq_num")]
SipCseqNumber,

[Description("sip_via_branch")]
SipViaBranch,

[Description("orig")]
OriginatingIdentityString,

[Description("dest")]
DestinationIdentityString,

[Description("mky")]
MediaKeyFingerprintString
}
}
22 changes: 22 additions & 0 deletions src/JWT/Builder/HeaderName.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.ComponentModel;

namespace JWT.Builder
{
/// <summary>
/// All predefined headers specified by RFC 7519, see https://tools.ietf.org/html/rfc7519
/// </summary>
/// <remarks>
/// Latest update: 31.10.2017
/// </remarks>
public enum HeaderName
{
[Description("typ")]
Type,

[Description("cty")]
ContentType,

[Description("alg")]
Algorithm
}
}
12 changes: 12 additions & 0 deletions src/JWT/Builder/Internal/DateTimeExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;
using System.Globalization;

namespace JWT.Builder.Internal
{
internal static class DateTimeExtensions
{
public static double GetSecondsSinceEpoch(this DateTime time) => Math.Round((time - JwtValidator.UnixEpoch).TotalSeconds);

public static string GetSecondsSinceEpochAsString(this DateTime time) => GetSecondsSinceEpoch(time).ToString(CultureInfo.InvariantCulture);
}
}
Loading

0 comments on commit 3e62ccd

Please sign in to comment.