C# OWIN OAuth2服务器:访问令牌始终返回invalid_grant

6ojccjat  于 2023-04-05  发布在  C#
关注(0)|答案(2)|浏览(232)

我正在按照下面的示例构建一个OAuth2授权服务器:https://learn.microsoft.com/en-us/aspnet/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server
我有一个授权码,并试图将其交换为访问令牌。无论如何,我得到了400响应“invalid_grant”。
以下是我的相关服务器实现:

OAuthAuthorizationServerOptions serverOptions = new OAuthAuthorizationServerOptions()
        {
            AuthorizeEndpointPath = new PathString(authorizePath),
            TokenEndpointPath = new PathString(tokenPath),
            ApplicationCanDisplayErrors = true,
            AllowInsecureHttp = true, // tsk tsk

            Provider = new OAuthAuthorizationServerProvider
            {
                //OnValidateAuthorizeRequest = ValidateAuth,
                OnValidateClientRedirectUri = ValidateClientRedirectUri,
                OnValidateClientAuthentication = ValidateClientAuthentication,
                OnGrantResourceOwnerCredentials = GrantResourceOwnerCredentials,
                OnGrantClientCredentials = GrantClientCredetails
            },
            // Authorization code provider which creates and receives authorization code
            AuthorizationCodeProvider = new AuthenticationTokenProvider
            {
                OnCreate = CreateAuthenticationCode,
                OnReceive = ReceiveAuthenticationCode
            },

            // Refresh token provider which creates and receives referesh token
            RefreshTokenProvider = new AuthenticationTokenProvider
            {
                OnCreate = CreateRefreshToken,
                OnReceive = ReceiveRefreshToken,
            }
        };

        app.UseOAuthAuthorizationServer(serverOptions);

    private Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
    {
        context.Validated(context.RedirectUri);
        return Task.FromResult(0);
    }

    private Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        string clientId;
        string clientSecret;
        if (context.TryGetBasicCredentials(out clientId, out clientSecret) ||
            context.TryGetFormCredentials(out clientId, out clientSecret))
        {
            context.Validated();
        }
        return Task.FromResult(0);
    }

    private readonly ConcurrentDictionary<string, string> _authenticationCodes =
       new ConcurrentDictionary<string, string>(StringComparer.Ordinal);
    private void CreateAuthenticationCode(AuthenticationTokenCreateContext context)
    {
        context.SetToken(Guid.NewGuid().ToString("n") + Guid.NewGuid().ToString("n"));
        _authenticationCodes[context.Token] = context.SerializeTicket();
    }

    private void ReceiveAuthenticationCode(AuthenticationTokenReceiveContext context)
    {
        string value;

        if (_authenticationCodes.TryRemove(context.Token, out value))
        {
            context.DeserializeTicket(value);
        }
        context.DeserializeTicket(value);
    }

    private void CreateRefreshToken(AuthenticationTokenCreateContext context)
    {
        context.SetToken(context.SerializeTicket());
    }

    private void ReceiveRefreshToken(AuthenticationTokenReceiveContext context)
    {
        context.DeserializeTicket(context.Token);
    }

下面是我在客户端请求令牌的地方:

var requestPrefix = Request.Scheme + "://" + Request.Host;
        var redirectUri = requestPrefix + Request.PathBase + Options.CallbackPath + "/";

        var body = new List<KeyValuePair<string, string>>
            {
                new KeyValuePair<string, string>("grant_type", "authorization_code"),
                new KeyValuePair<string, string>("code", code),
                new KeyValuePair<string, string>("redirect_uri", redirectUri),
                new KeyValuePair<string, string>("client_id", Options.ClientId),
                new KeyValuePair<string, string>("client_secret", Options.ClientSecret),

            };

        HttpClient _httpClient = new HttpClient();

        var tokenResponse =
            await _httpClient.PostAsync(Constants.GARBAGE_TOKEN, new FormUrlEncodedContent(body));

        var text = await tokenResponse.Content.ReadAsStringAsync();

到目前为止我测试的是:

  • 我已经在所有这些提供程序函数中添加了断点并添加了上下文。在我可以验证的任何地方都进行了验证。以前,它没有命中ReceiveAuthenticationCode,因为上下文没有在ValidateClientAuthentication中进行验证。这让我假设我在某个地方错过了验证步骤,但我找不到其他任何地方。
  • 现在它确实到达了ReceiveAuthenticationCode,获得了令牌,并反序列化了票证。Context.Identity.Ticket不是null,并且有数据,所以我假设它无论如何都是正确的。

但是在那之后-我就失去了它的踪迹。它作为“invalid_grant”返回到调用客户端,我绞尽脑汁地试图弄清楚令牌接下来在管道中的位置。据推测,由于某种原因,正在调用某个提供者元素,该元素将令牌设置为无效,但是我一直在浏览OAuthAuthorizationServerProvider源代码,什么都没有,而且似乎没有任何方法可以遍历它。事实上,即使知道处理发出令牌的实际响应的代码部分也将是有用的;这对我来说都是不透明的。
为什么令牌会失效?它是真的失效了,还是其他错误导致它返回invalid_grant?如何查看这部分?
我还看了微软文档中的例子,在开始时创建一个OAuth2服务器链接,其中大部分都是基于这个例子。但是它没有帮助,因为它使用了这个helper方法,它封装了所有内容:

var authorizationState = _webServerClient.ProcessUserAuthorization(Request);

所以这并没有告诉我太多,我也不确定上面的助手是如何工作的,因为它似乎是在一个地方处理授权代码提交和令牌请求的。

bwntbbo3

bwntbbo31#

您可以启用OWIN跟踪,以便可能获得更具描述性的错误消息。
将以下内容添加到您的app.config:

<system.diagnostics>
    <switches>
        <add name="Microsoft.Owin" value="All" />
    </switches>
    <trace autoflush="true" />
    <sharedListeners>
        <add name="file" type="System.Diagnostics.TextWriterTraceListener" initializeData="OWINtrace.log" />
    </sharedListeners>
    <sources>
        <source name="Microsoft.Owin" switchName="Microsoft.Owin" switchType="System.Diagnostics.SourceSwitch">
            <listeners>
                <add name="file" />
            </listeners>
        </source>
    </sources>
</system.diagnostics>

重现问题后,检查OWINtrace.log。它应该在可执行文件所在的文件夹中。

i5desfxk

i5desfxk2#

在请求访问令牌时尝试使用POST请求。

相关问题