OWIN OAuth2 中间件在重定向 uri 中带有 %23(# 片段)

OWIN OAuth2 middleware with %23 (# fragment) in redirect uri

我正在使用 ASP.NET OWIN/Katana OAuthAuthorizationServer 中间件,运行 遇到了麻烦。

我有一些站点试图获取授权令牌并创建了一个 /oauth/authorize 端点。但是,这些请求来自 SPA (Angular),并且在重定向 URL.

中通常会有一个片段 (#)

发出请求时,它会将 redirect_uri 设置为 URL,但 URL 编码(因此 # 更改为 %23 ).但是,每当 %23 在 URL 中出现时,状态总是设置为 400,我似乎无法以任何方式阻止这种情况......无法覆盖任何东西并且没有 Web.config保留坏字符修改等

因此,我试图用一些占位符替换它,然后重定向回它自己。这很好用。但是,我似乎无法撤消我的 URL 更改。我需要将 # 放回 URL 并重定向到那里,但是 OWIN 中间件完全忽略了将 URL 改回去的任何尝试...有人有任何想法吗?

井号后的部分 https://en.wikipedia.org/wiki/Fragment_identifier never to be sent to server - see for example Can I read the hash portion of the URL on my server-side application (PHP, Ruby, Python, etc.)? 以及此处的许多类似问题。

您可以做的是 post 通过 Java 脚本在客户端处理您的 placehodler,而不是尝试使用其中的 # 重定向到 URL。

这是 RFC 6749 Section 3.1.2 施加的限制:

The endpoint URI MUST NOT include a fragment component.

基于 OAuth2 规范的 OpenID Connect 规范(以及流行的 IdentityServer3/4)似乎没有此限制,因此您可以切换到 OIDC 或稍微改变 OAuth2 规范:)

在 OWIN 中间件中,片段的存在在 OAuthAuthorizationServerHandler 中检查。

A work-around 是通过用占位符替换表示片段的 %23 来绕过此检查,并在发送到浏览器之前修补位置重定向 header。

要用占位符替换传入的 %23,您可以重写提供者 MatchEndpoint 方法:

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);
    }
}

另一部分比较棘手,因为您似乎无法从提供程序 class 默认情况下执行此操作,因为 redirectUri 在内部保存在 OAuthValidateClientRedirectUriContext.

一种 hacky 方法是使用反射并在提供程序 ValidateClientRedirectUri 方法中修改此 RedirectUri

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 中间件之前)以监视响应(因此在等待下一个中间件之后)并在需要时修补位置 header。这比设置私有 属性 更简单,但现在补丁分布在 2 个不同的地方,难以维护。

上面 work-around 一个像

这样的重定向 URI
https://server.com/#spa/path/123

请注意,您会将 URL-encoded 版本传递给授权端点 redirect_uri param

将重定向到

https://server.com/#spa/path/123&access_token=<the_access_token>