diff --git a/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs b/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs index 7f59443bf3..c31f6e29ed 100644 --- a/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Net.Http; using System.Net.Http.Headers; using System.Security.Claims; @@ -108,8 +109,15 @@ namespace Microsoft.AspNetCore.Authentication.OAuth if (!string.IsNullOrEmpty(tokens.ExpiresIn)) { - identity.AddClaim(new Claim("expires_in", tokens.ExpiresIn, - ClaimValueTypes.String, Options.ClaimsIssuer)); + int value; + if (int.TryParse(tokens.ExpiresIn, NumberStyles.Integer, CultureInfo.InvariantCulture, out value)) + { + var expiresAt = Options.SystemClock.UtcNow + TimeSpan.FromSeconds(value); + // https://www.w3.org/TR/xmlschema-2/#dateTime + // https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx + identity.AddClaim(new Claim("expires_at", expiresAt.ToString("o", CultureInfo.InvariantCulture), + ClaimValueTypes.DateTime, Options.ClaimsIssuer)); + } } } diff --git a/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthOptions.cs b/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthOptions.cs index 230c7362ad..a094e681ba 100644 --- a/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthOptions.cs +++ b/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthOptions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; +using System.ComponentModel; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.OAuth; using Microsoft.AspNetCore.Http.Authentication; @@ -63,5 +64,11 @@ namespace Microsoft.AspNetCore.Builder /// Gets or sets the type used to secure data handled by the middleware. /// public ISecureDataFormat StateDataFormat { get; set; } + + /// + /// For testing purposes only. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public ISystemClock SystemClock { get; set; } = new SystemClock(); } } diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs index 8ade6ee631..379beba2de 100644 --- a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs @@ -778,8 +778,15 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect if (!string.IsNullOrEmpty(message.ExpiresIn)) { - identity.AddClaim(new Claim(OpenIdConnectParameterNames.ExpiresIn, message.ExpiresIn, - ClaimValueTypes.String, issuer)); + int value; + if (int.TryParse(message.ExpiresIn, NumberStyles.Integer, CultureInfo.InvariantCulture, out value)) + { + var expiresAt = Options.SystemClock.UtcNow + TimeSpan.FromSeconds(value); + // https://www.w3.org/TR/xmlschema-2/#dateTime + // https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx + identity.AddClaim(new Claim("expires_at", expiresAt.ToString("o", CultureInfo.InvariantCulture), + ClaimValueTypes.DateTime, issuer)); + } } } diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectOptions.cs b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectOptions.cs index 4ae05a2bba..9e8eef1e19 100644 --- a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectOptions.cs +++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectOptions.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.IdentityModel.Tokens.Jwt; using Microsoft.AspNetCore.Authentication; @@ -191,5 +192,11 @@ namespace Microsoft.AspNetCore.Builder /// This is disabled by default. /// public bool SkipUnrecognizedRequests { get; set; } = false; + + /// + /// For testing purposes only. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public ISystemClock SystemClock { get; set; } = new SystemClock(); } }