这是RFC 6749 第 3.1.2 节施加的限制:
端点 URI 不得包含片段组件。
基于 OAuth2 规范的 OpenID Connect 规范(以及流行的 IdentityServer3/4)似乎没有这个限制,因此您可以切换到 OIDC 或稍微改变 OAuth2 规范 :)
在 OWIN 中间件中,在OAuthAuthorizationServerHandler中检查片段的存在。
一种解决方法是通过用%23
占位符替换表示片段的方式绕过此检查,并在将位置重定向标头发送到浏览器之前对其进行修补。
%23
要使用占位符替换传入,您可以覆盖 providersMatchEndpoint
方法:
internal class OAuth2AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override Task MatchEndpoint(OAuthMatchEndpointContext context)
{
if (context.Request.Path.StartsWithSegments(context.Options.AuthorizeEndpointPath)
&& context.Request.QueryString.HasValue)
{
context.Request.QueryString = new QueryString(
context.Request.QueryString.Value.Replace("%23", "__fragment__"));
}
return base.MatchEndpoint(context);
}
}
另一部分比较棘手,因为默认情况下您似乎无法从提供程序类中执行此操作,因为它redirectUri
内部保存在OAuthValidateClientRedirectUriContext上。
RedirectUri
一个 hacky 方法是使用反射并在 providersValidateClientRedirectUri
方法中修改它:
public override async Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
// Test if the redirect uri is allowed, if not call context.SetError("invalid_client");
var redirectUri = context.RedirectUri.Replace("__fragment__", "#");
var setter = context.GetType().GetProperty(nameof(context.RedirectUri))?.GetSetMethod(true);
setter?.Invoke(context, new[] { redirectUri });
context.Validated(redirectUri);
}
另一种方法是向链中添加一个简单的中间件(在 OAuth2 中间件之前),以监控响应(因此在等待下一个中间件之后)并在需要的地方修补 Location 标头。这比设置私有属性要简单,但现在补丁分布在 2 个不同的地方,不易维护。
通过上述解决方法,重定向 URI 像
https://server.com/#spa/path/123
请注意,您会将 URL 编码的版本传递给授权端点 redirect_uri 参数
将导致重定向到
https://server.com/#spa/path/123&access_token=<the_access_token>