From 52b623770d22977084a278110c92073152e2e9ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Fri, 30 Aug 2024 15:55:16 +0200 Subject: [PATCH] Use the current address as the default target link URI when AuthenticationProperties.RedirectUri is not set --- .../OpenIddictClientAspNetCoreHandlers.cs | 38 ++++++++++++++++++- .../OpenIddictClientOwinHandlers.cs | 32 +++++++++++++++- 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.cs b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.cs index 547afc90..3af8188c 100644 --- a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.cs +++ b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.cs @@ -564,6 +564,11 @@ public static partial class OpenIddictClientAspNetCoreHandlers throw new ArgumentNullException(nameof(context)); } + // This handler only applies to ASP.NET Core requests. If the HTTP context cannot be resolved, + // this may indicate that the request was incorrectly processed by another server stack. + var request = context.Transaction.GetHttpRequest() ?? + throw new InvalidOperationException(SR.GetResourceString(SR.ID0114)); + var properties = context.Transaction.GetProperty(typeof(AuthenticationProperties).FullName!); if (properties is { Items.Count: > 0 }) { @@ -575,7 +580,19 @@ public static partial class OpenIddictClientAspNetCoreHandlers context.RegistrationId = GetProperty(properties, Properties.RegistrationId); context.ResponseMode = GetProperty(properties, Properties.ResponseMode); context.ResponseType = GetProperty(properties, Properties.ResponseType); - context.TargetLinkUri = properties.RedirectUri; + + context.TargetLinkUri = properties.RedirectUri switch + { + // If a return URL - local or not - was explicitly set in the authentication properties, always honor it. + { Length: > 0 } uri => uri, + + // If no return URL was explicitly set in the authentication properties (e.g because + // the challenge was triggered automatically by ASP.NET Core or because no return URL + // was specified by the user), use the current location as the default target link URI. + _ => (request.HttpContext.Features.Get()?.OriginalPathBase ?? request.PathBase) + + (request.HttpContext.Features.Get()?.OriginalPath ?? request.Path) + + request.QueryString + }; if (properties.Items.TryGetValue(Properties.Issuer, out string? issuer) && !string.IsNullOrEmpty(issuer)) { @@ -880,6 +897,11 @@ public static partial class OpenIddictClientAspNetCoreHandlers throw new ArgumentNullException(nameof(context)); } + // This handler only applies to ASP.NET Core requests. If the HTTP context cannot be resolved, + // this may indicate that the request was incorrectly processed by another server stack. + var request = context.Transaction.GetHttpRequest() ?? + throw new InvalidOperationException(SR.GetResourceString(SR.ID0114)); + var properties = context.Transaction.GetProperty(typeof(AuthenticationProperties).FullName!); if (properties is { Items.Count: > 0 }) { @@ -887,7 +909,19 @@ public static partial class OpenIddictClientAspNetCoreHandlers context.LoginHint = GetProperty(properties, Properties.LoginHint); context.ProviderName = GetProperty(properties, Properties.ProviderName); context.RegistrationId = GetProperty(properties, Properties.RegistrationId); - context.TargetLinkUri = properties.RedirectUri; + + context.TargetLinkUri = properties.RedirectUri switch + { + // If a return URL - local or not - was explicitly set in the authentication properties, always honor it. + { Length: > 0 } uri => uri, + + // If no return URL was explicitly set in the authentication properties (e.g because + // the challenge was triggered automatically by ASP.NET Core or because no return URL + // was specified by the user), use the current location as the default target link URI. + _ => (request.HttpContext.Features.Get()?.OriginalPathBase ?? request.PathBase) + + (request.HttpContext.Features.Get()?.OriginalPath ?? request.Path) + + request.QueryString + }; if (properties.Items.TryGetValue(Properties.Issuer, out string? issuer) && !string.IsNullOrEmpty(issuer)) { diff --git a/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.cs b/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.cs index b8159abb..b7821699 100644 --- a/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.cs +++ b/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.cs @@ -573,6 +573,11 @@ public static partial class OpenIddictClientOwinHandlers throw new ArgumentNullException(nameof(context)); } + // This handler only applies to OWIN requests. If the HTTP context cannot be resolved, + // this may indicate that the request was incorrectly processed by another server stack. + var request = context.Transaction.GetOwinRequest() ?? + throw new InvalidOperationException(SR.GetResourceString(SR.ID0120)); + var properties = context.Transaction.GetProperty(typeof(AuthenticationProperties).FullName!); if (properties is not { Dictionary.Count: > 0 }) { @@ -587,7 +592,16 @@ public static partial class OpenIddictClientOwinHandlers context.RegistrationId = GetProperty(properties, Properties.RegistrationId); context.ResponseMode = GetProperty(properties, Properties.ResponseMode); context.ResponseType = GetProperty(properties, Properties.ResponseType); - context.TargetLinkUri = properties.RedirectUri; + + context.TargetLinkUri = properties.RedirectUri switch + { + // If a return URL - local or not - was explicitly set in the authentication properties, always honor it. + { Length: > 0 } uri => uri, + + // If no return URL was explicitly set in the authentication properties (e.g because no return + // URL was specified by the user), use the current address as the default target link URI. + _ => request.PathBase + request.Path + request.QueryString + }; if (properties.Dictionary.TryGetValue(Properties.Issuer, out string? issuer) && !string.IsNullOrEmpty(issuer)) { @@ -915,6 +929,11 @@ public static partial class OpenIddictClientOwinHandlers throw new ArgumentNullException(nameof(context)); } + // This handler only applies to OWIN requests. If the HTTP context cannot be resolved, + // this may indicate that the request was incorrectly processed by another server stack. + var request = context.Transaction.GetOwinRequest() ?? + throw new InvalidOperationException(SR.GetResourceString(SR.ID0120)); + var properties = context.Transaction.GetProperty(typeof(AuthenticationProperties).FullName!); if (properties is not { Dictionary.Count: > 0 }) { @@ -925,7 +944,16 @@ public static partial class OpenIddictClientOwinHandlers context.LoginHint = GetProperty(properties, Properties.LoginHint); context.ProviderName = GetProperty(properties, Properties.ProviderName); context.RegistrationId = GetProperty(properties, Properties.RegistrationId); - context.TargetLinkUri = properties.RedirectUri; + + context.TargetLinkUri = properties.RedirectUri switch + { + // If a return URL - local or not - was explicitly set in the authentication properties, always honor it. + { Length: > 0 } uri => uri, + + // If no return URL was explicitly set in the authentication properties (e.g because no return + // URL was specified by the user), use the current address as the default target link URI. + _ => request.PathBase + request.Path + request.QueryString + }; if (properties.Dictionary.TryGetValue(Properties.Issuer, out string? issuer) && !string.IsNullOrEmpty(issuer)) {