Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding 'iat' (issued at) claim to tokens #342

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/D2L.Security.OAuth2/Keys/Default/TokenSigner.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IdentityModel.Tokens.Jwt;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
Expand Down Expand Up @@ -36,6 +37,9 @@ async Task<string> ITokenSigner.SignAsync( UnsignedToken token ) {
signingCredentials: securityToken.GetSigningCredentials()
);

DateTimeOffset issuedAtOffset = token.IssuedAt;
jwt.Payload.Add( OAuth2.Constants.Claims.ISSUED_AT, issuedAtOffset.ToUnixTimeSeconds() );

var claims = token.Claims;
foreach( var claim in claims ) {
if( jwt.Payload.ContainsKey( claim.Key ) ) {
Expand Down
13 changes: 12 additions & 1 deletion src/D2L.Security.OAuth2/Keys/UnsignedToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public sealed class UnsignedToken {
private readonly IReadOnlyDictionary<string, object> m_claims;
private readonly DateTime m_notBefore;
private readonly DateTime m_expiresAt;
private readonly DateTime m_issuedAt;

/// <summary>
/// Constructs a new <see cref="UnsignedToken"/> instance
Expand All @@ -26,13 +27,15 @@ public UnsignedToken(
string audience,
IReadOnlyDictionary<string, object> claims,
DateTime notBefore,
DateTime expiresAt
DateTime expiresAt,
DateTime issuedAt
) {
m_issuer = issuer;
m_audience = audience;
m_claims = claims;
m_notBefore = notBefore;
m_expiresAt = expiresAt;
m_issuedAt = issuedAt;
}

/// <summary>
Expand Down Expand Up @@ -69,5 +72,13 @@ public DateTime NotBefore {
public DateTime ExpiresAt {
get { return m_expiresAt; }
}

/// <summary>
/// When the token was issued; this is the 'iat' claim
/// </summary>
public DateTime IssuedAt {
get { return m_issuedAt; }
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ IEnumerable<Scope> scopes
audience: Constants.ASSERTION_AUDIENCE,
claims: filteredClaims,
notBefore: now,
expiresAt: now + Constants.ASSERTION_TOKEN_LIFETIME );
expiresAt: now + Constants.ASSERTION_TOKEN_LIFETIME,
issuedAt: now );

string assertion = await m_tokenSigner
.SignAsync( unsignedToken )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ private void SetUp( out Uri host, out string token, out string id ) {
"some audience",
new Dictionary<string, object>(),
DateTime.Now,
DateTime.Now + TimeSpan.FromDays( 1 )
DateTime.Now + TimeSpan.FromDays( 1 ),
DateTime.Now
) )
.ConfigureAwait( false )
.GetAwaiter()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public async Task ValidateAsync_GoodSignature_Succeeds() {
"fake audience",
new Dictionary<string, object> { { "sub", SUBJECT } },
DateTime.UtcNow - TimeSpan.FromSeconds( 1 ),
DateTime.UtcNow + TimeSpan.FromHours( 1 ) ) )
DateTime.UtcNow + TimeSpan.FromHours( 1 ),
DateTime.UtcNow - TimeSpan.FromSeconds( 1 ) ) )
.ConfigureAwait( false );

IAccessToken accessToken = await m_accessTokenValidator
Expand All @@ -63,7 +64,8 @@ public async Task ValidateAsync_BadSignature_Fails() {
"fake audience",
new Dictionary<string, object>(),
DateTime.UtcNow - TimeSpan.FromSeconds( 1 ),
DateTime.UtcNow + TimeSpan.FromHours( 1 ) ) )
DateTime.UtcNow + TimeSpan.FromHours( 1 ),
DateTime.UtcNow - TimeSpan.FromSeconds( 1 ) ) )
.ConfigureAwait( false );

token += "abcd";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public async Task ValidateAsync_GoodSignature_Succeeds() {
"fake audience",
new Dictionary<string, object> { { "sub", SUBJECT } },
DateTime.UtcNow - TimeSpan.FromSeconds( 1 ),
DateTime.UtcNow + TimeSpan.FromHours( 1 ) ) )
DateTime.UtcNow + TimeSpan.FromHours( 1 ),
DateTime.UtcNow - TimeSpan.FromSeconds( 1 ) ) )
.ConfigureAwait( false );

IAccessToken accessToken = await m_accessTokenValidator
Expand All @@ -63,7 +64,8 @@ public async Task ValidateAsync_BadSignature_Fails() {
"fake audience",
new Dictionary<string, object>(),
DateTime.UtcNow - TimeSpan.FromSeconds( 1 ),
DateTime.UtcNow + TimeSpan.FromHours( 1 ) ) )
DateTime.UtcNow + TimeSpan.FromHours( 1 ),
DateTime.UtcNow - TimeSpan.FromSeconds( 1 ) ) )
.ConfigureAwait( false );

token += "abcd";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ namespace D2L.Security.OAuth2.Keys.Default {
internal sealed class TokenSignerTests {
private static readonly string TestKeyId = TestStaticKeyProvider.TestKeyId;

private const string SignedToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImZhN2MwN2E4LTQyYzgtNGM1Ny05YWYyLWNjZTEwYzI3MTAzMyIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE1NDYzMDA4MDAsImV4cCI6MTU0NjMwNDQzMCwiaXNzIjoiaXNzdWVyIiwiYXVkIjoiYXVkaWVuY2UiLCJzY29wZXMiOiJhOmI6YyBhOmI6ZCIsInRlbmFudGlkIjoiMzI1Y2I0NmItNDg4ZC00MDYxLWFhMmMtZWVmNWExMmI2YjdjIn0.HB_hkyPQE2jfhTaRf64qqWjo6HkJcZILYGnjccsXTrjJpHTrxPlu7gmmolTXIBTplKlB08O2Q-Q9NHtDg5XtCTsm_PNjy6G8OJlu1NEMMQUS-V7phNVpOGxIMQamj_5jI8uz1Xx2nzj333mE9tJXvuba8GWeTbPlYKLd7kI83wGAYVNAUxV6ZkaoYu5Uvj3NRByAeXtDhXdhdk6UPeHECfaei3OE6NlGwuJIB-pts3V4JDq0xOIUCt84lFy27aiQyILXoKrwPdkboFxXRthtLw6aIl_Ce8fSCjAxDmMNkV6pW0FCCl_nWrVooTeXEcDkCr5c7NRRyAOHky-n5WDISQ";
private const string SignedComplexToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImZhN2MwN2E4LTQyYzgtNGM1Ny05YWYyLWNjZTEwYzI3MTAzMyIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE1NDYzMDA4MDAsImV4cCI6MTU0NjMwNDQzMCwiaXNzIjoiaXNzdWVyIiwiYXVkIjoiYXVkaWVuY2UiLCJodHRwczovL3NlcnZpY2UuY29tL2NsYWltL3ZlcnNpb24iOiIxLjAuMCIsImh0dHBzOi8vc2VydmljZS5jb20vY2xhaW0vcm9sZXMiOlsiaHR0cHM6Ly9zZXJ2aWNlLmNvbS9yb2xlcyNhZG1pbmlzdHJhdG9yIiwiaHR0cHM6Ly9zZXJ2aWNlLmNvbS9yb2xlcyN1c2VyIl0sImh0dHBzOi8vc2VydmljZS5jb20vY2xhaW0vY29udGV4dCI6eyJpZCI6ImMxZDg4N2YwLWExYTMtNGJjYS1hZTI1LWMzNzVlZGNjMTMxYSIsInR5cGUiOlsiaHR0cHM6Ly9zZXJ2aWNlLmNvbS90eXBlcyN0eXBlIl19fQ.eMvY5Srt0I-tQ-4gWi5EXmZ897IVymLSywUd6DI6QYcD4zCVoNMus7VbwcJm8P3Sb6-JUHS7ZMBuaA3E0xG4xmPtrnCUkEETfKgjQmmHGHwx0ZoBnsjYhTRUk_imf5DMPWIUS8S01QYz1pbFhxsKQGX2h-dTpkPa_PH2vCd-5TlKKGrOCkV4c60RpkQuj1UVcxpHpCecPGT-ulIA7loK2mCSov5lk_hvnmvFTH-F0eOf-CI9ebDvfWCSnKGlsBp6TAo9PIDDtH4GiwLrW6ZsSGMXGE5uWLFDGtZq-OETV6EMEOA-N4MJJ_34tJ0SPVSJ_8ApyP8_HBzGb9ED8PQBOQ";
private const string SignedToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImZhN2MwN2E4LTQyYzgtNGM1Ny05YWYyLWNjZTEwYzI3MTAzMyIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE1NDYzMDA4MDAsImV4cCI6MTU0NjMwNDQzMCwiaXNzIjoiaXNzdWVyIiwiYXVkIjoiYXVkaWVuY2UiLCJpYXQiOjE1NDYzMDA4MDAsInNjb3BlcyI6ImE6YjpjIGE6YjpkIiwidGVuYW50aWQiOiIzMjVjYjQ2Yi00ODhkLTQwNjEtYWEyYy1lZWY1YTEyYjZiN2MifQ.eqH5TlV5TN4w4b9RoIUBaeTlpkyQ15z2MAXRdh7smn90ZN0wxHtThxqUGfdLsFWqbn5e1hVS69-wQcDBBwl5m0p1AgCS1LSHd9rT4Eh6lM9_A_NoLamF5LGBZqm_SoE4DaLLUgwYc2YbOFs577AU34WmgE6oZaG_j6JMzxAs8lgyhpj-iFME2mSJTpUK4H1RgQ03my0zpUvuKFFS2NJfPF2Vs_aXh0L1PKV6MYoNMJHZmEyLSpj36R8zIPyFGkbrxztqmTsg08yKjHiZgp9FBx_sDm-nwAMlpSIn4_xpfK4U50mLkIsejvr86gSqVcgHht8Au5FYY8Y4x2MDs8jAJg";
private const string SignedComplexToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImZhN2MwN2E4LTQyYzgtNGM1Ny05YWYyLWNjZTEwYzI3MTAzMyIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE1NDYzMDA4MDAsImV4cCI6MTU0NjMwNDQzMCwiaXNzIjoiaXNzdWVyIiwiYXVkIjoiYXVkaWVuY2UiLCJpYXQiOjE1NDYzMDA4MDAsImh0dHBzOi8vc2VydmljZS5jb20vY2xhaW0vdmVyc2lvbiI6IjEuMC4wIiwiaHR0cHM6Ly9zZXJ2aWNlLmNvbS9jbGFpbS9yb2xlcyI6WyJodHRwczovL3NlcnZpY2UuY29tL3JvbGVzI2FkbWluaXN0cmF0b3IiLCJodHRwczovL3NlcnZpY2UuY29tL3JvbGVzI3VzZXIiXSwiaHR0cHM6Ly9zZXJ2aWNlLmNvbS9jbGFpbS9jb250ZXh0Ijp7ImlkIjoiYzFkODg3ZjAtYTFhMy00YmNhLWFlMjUtYzM3NWVkY2MxMzFhIiwidHlwZSI6WyJodHRwczovL3NlcnZpY2UuY29tL3R5cGVzI3R5cGUiXX19.FjG7jBeAZQGjdxkTIjoGjbtf-Jz89Tje7TWSdy401NDI5ns7c6MORrpNHqCIUs6PO4zbHnmf1SBVAC2ALJWEJ4K5KH4WlZxm9VYyUQ7xrhWQurGp7szK9pUmJqvb0mTB9fOg-BMjICknfHxXnkXIR9tYL8MEbK3jaz1wDWF4V6qlTY-TcHt4mDbK5PG4e1K-ZgSG1Jgci_avtYh6gv9BqGqYB0wyu5OKwRTXrtjv5roDqNFO9G_aPUzmzI_9IgwLqJ3XahSbwgHLr49yW6kL0N01rWHvi1Clb3fP7Jl9ec-ANJkKPFXea4ZsdmZrlBdHv3G94JkaDf9aWaSUxNWZSQ";

private IPrivateKeyProvider m_privateKeyProvider;
private ITokenSigner m_tokenSigner;
Expand All @@ -36,8 +36,9 @@ public async Task SignsUnsignedToken() {
{ "scopes", "a:b:c a:b:d" },
{ "tenantid", "325cb46b-488d-4061-aa2c-eef5a12b6b7c" }
},
notBefore: new DateTime( 2019, 1, 1, 0, 0, 0, DateTimeKind.Utc ),
expiresAt: new DateTime( 2019, 1, 1, 1, 0, 30, DateTimeKind.Utc )
notBefore: new DateTime( 2019, 1, 1, 0, 0, 0, DateTimeKind.Utc ),
expiresAt: new DateTime( 2019, 1, 1, 1, 0, 30, DateTimeKind.Utc ),
issuedAt: new DateTime( 2019, 1, 1, 0, 0, 0, DateTimeKind.Utc )
);

var signed = await m_tokenSigner.SignAsync( token );
Expand All @@ -62,8 +63,9 @@ public async Task SignsUnsignedToken_WithComplexClaims() {
issuer: "issuer",
audience: "audience",
claims: claims,
notBefore: new DateTime( 2019, 1, 1, 0, 0, 0, DateTimeKind.Utc ),
expiresAt: new DateTime( 2019, 1, 1, 1, 0, 30, DateTimeKind.Utc )
notBefore: new DateTime( 2019, 1, 1, 0, 0, 0, DateTimeKind.Utc ),
expiresAt: new DateTime( 2019, 1, 1, 1, 0, 30, DateTimeKind.Utc ),
issuedAt: new DateTime( 2019, 1, 1, 0, 0, 0, DateTimeKind.Utc )
);

var signed = await m_tokenSigner.SignAsync( token );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ public static async Task<string> GetAccessTokenValidForAMinute(
audience: Constants.ACCESS_TOKEN_AUDIENCE,
claims: claims,
notBefore: issuedAtTime.Value,
expiresAt: issuedAtTime.Value + TimeSpan.FromMinutes( 1 )
expiresAt: issuedAtTime.Value + TimeSpan.FromMinutes( 1 ),
issuedAt: issuedAtTime.Value
)
).ConfigureAwait( false );
}
Expand Down