diff --git a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreConstants.cs b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreConstants.cs index ccc88f47..0791e7b3 100644 --- a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreConstants.cs +++ b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreConstants.cs @@ -35,8 +35,10 @@ public static class OpenIddictClientAspNetCoreConstants { public const string AuthorizationCode = "authorization_code"; public const string BackchannelAccessToken = "backchannel_access_token"; + public const string BackchannelAccessTokenExpirationDate = "backchannel_access_token_expiration_date"; public const string BackchannelIdentityToken = "backchannel_id_token"; public const string FrontchannelAccessToken = "frontchannel_access_token"; + public const string FrontchannelAccessTokenExpirationDate = "frontchannel_access_token_expiration_date"; public const string FrontchannelIdentityToken = "frontchannel_id_token"; public const string RefreshToken = "refresh_token"; public const string StateToken = "state_token"; diff --git a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandler.cs b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandler.cs index 7dffffe7..6cb40923 100644 --- a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandler.cs +++ b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandler.cs @@ -6,6 +6,7 @@ using System.ComponentModel; using System.Diagnostics; +using System.Globalization; using System.Security.Claims; using System.Text.Encodings.Web; using System.Text.Json; @@ -192,6 +193,9 @@ public sealed class OpenIddictClientAspNetCoreHandler : AuthenticationHandler public string? BackchannelAccessToken { get; set; } + /// + /// Gets or sets the expiration date of the backchannel access token, if applicable. + /// + public DateTimeOffset? BackchannelAccessTokenExpirationDate { get; set; } + /// /// Gets or sets the backchannel identity token to validate, if applicable. /// @@ -698,6 +703,11 @@ public static partial class OpenIddictClientEvents /// public string? FrontchannelAccessToken { get; set; } + /// + /// Gets or sets the expiration date of the frontchannel access token, if applicable. + /// + public DateTimeOffset? FrontchannelAccessTokenExpirationDate { get; set; } + /// /// Gets or sets the frontchannel identity token to validate, if applicable. /// diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.cs index 02c64733..43f1224f 100644 --- a/src/OpenIddict.Client/OpenIddictClientHandlers.cs +++ b/src/OpenIddict.Client/OpenIddictClientHandlers.cs @@ -1392,6 +1392,15 @@ public static partial class OpenIddictClientHandlers _ => null }; + context.FrontchannelAccessTokenExpirationDate = context.EndpointType switch + { + OpenIddictClientEndpointType.Redirection when context.ExtractFrontchannelAccessToken + => ((long?) context.Request[Parameters.ExpiresIn]) is long value ? + DateTimeOffset.UtcNow.AddSeconds(value) : null, + + _ => null + }; + context.FrontchannelIdentityToken = context.EndpointType switch { OpenIddictClientEndpointType.Redirection when context.ExtractFrontchannelIdentityToken @@ -2721,9 +2730,18 @@ public static partial class OpenIddictClientHandlers Debug.Assert(context.TokenResponse is not null, SR.GetResourceString(SR.ID4007)); - context.BackchannelAccessToken = context.ExtractBackchannelAccessToken ? context.TokenResponse.AccessToken : null; - context.BackchannelIdentityToken = context.ExtractBackchannelIdentityToken ? context.TokenResponse.IdToken : null; - context.RefreshToken = context.ExtractRefreshToken ? context.TokenResponse.RefreshToken : null; + context.BackchannelAccessToken = context.ExtractBackchannelAccessToken ? + context.TokenResponse.AccessToken : null; + + context.BackchannelAccessTokenExpirationDate = + context.ExtractBackchannelAccessToken && + context.TokenResponse.ExpiresIn is long value ? DateTimeOffset.UtcNow.AddSeconds(value) : null; + + context.BackchannelIdentityToken = context.ExtractBackchannelIdentityToken ? + context.TokenResponse.IdToken : null; + + context.RefreshToken = context.ExtractRefreshToken ? + context.TokenResponse.RefreshToken : null; return default; }