AuthorizationCodeProvider:从不调用Create,如何生成授权码?

时间:2019-04-17 08:05:22

标签: oauth-2.0 asp.net-web-api2 thinktecture-ident-model

我正在设置自己的OAuth2服务器。到目前为止,我已经在GrantResourceOwnerCredentials的实现中成功实现了OAuthAuthorizationServerProvider。现在,因为我正在为我们的业务开发应用程序,所以我想实现OAuth2授权代码授予。

我尝试遵循此处的说明https://docs.microsoft.com/en-us/aspnet/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server,但是在实现中,我没有找到如何到达Create的{​​{1}}调用(我在{{1}中进行了设置) }。

我已经简短地检查了使用(错误的)代码参数访问AuthorizationCodeProvider是否可行,并且在调试器中,我看到了OAuthAuthorizationServerOptions的{​​{1}}调用被击中。当然不会成功,因为我发送的代码是“ sometestcode”而不是真实的代码,但是代码被命中,这意味着我处在正确的道路上。

这是我到目前为止所拥有的:

TokenEndpointPath

现在,当我用AuthorizationCodeProvider呼叫我的Receive时,我会立即被发送到那个 Uri。我知道这是错误的:应该将我发送到单独的登录页面。稍后我将修复Web API,以重定向到正确的Uri。

我的问题的重点是:我现在正在实现登录页面,但是在用户登录后,我不知道如何从WebAPI获取授权代码。(我跳过了暂时同意),并假设如果用户登录后可以接受,我将在以后添加给予同意。)

我的流程基于此处https://docs.apigee.com/api-platform/security/oauth/oauth-v2-policy-authorization-code-grant-type共享的图

我正在使用Thinktecture IdentityModel在MVC控制器中创建登录页面。现在,我需要从MVC控制器中的Web API检索授权代码。之后,我可以将用户重定向回请求授权码流的原始客户端(应用)。

要从我的Web API获取授权代码,我在Thinktecture的OAuth2Client中看到了三种方法:

  • CreateAuthorizeUrl
  • CreateCodeFlowUrl
  • RequestAuthorizationCodeAsync

似乎都没有做我想要的。如何进行操作,以便调用我的WebAPI生成代码?

    public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
    {
        if (OAuthRepository.GetClient(context.ClientId) != null)
        {
            var expectedRootUri = new Uri(context.Request.Uri, "/");

            if (context.RedirectUri.StartsWith(expectedRootUri.AbsoluteUri))
            {
                context.Validated();
                return Task.FromResult<object>(null);
            }
        }

        context.Rejected();
        return Task.FromResult<object>(null);
    }

    public override Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
    {
        // I know this is wrong but it's just a start and not the focus of this SO question.
        context.Response.Redirect(context.AuthorizeRequest.RedirectUri);
        context.RequestCompleted();
        return Task.FromResult<object>(null);
    }

    public override Task GrantAuthorizationCode(OAuthGrantAuthorizationCodeContext context)
    {
        // Needs additional checks, not the focus of my question either 
        var newTicket = new AuthenticationTicket(context.Ticket.Identity, context.Ticket.Properties);
        context.Validated(newTicket);
        return Task.FromResult<object>(null);
    }

我认为我已经在Web API端正确设置了它。我只是不知道如何实现流程的AuthorizeEndpointPath部分。我希望有人能帮助我理解我所没有看到的。我认为某个地方有一个盲点...

我如何让OAuth2Client从我的WebAPI获得授权代码?

使用Postman测试我的Web API。如果有人可以帮助我获得返回授权码的Web API 2.0中的URL,我也将其作为答案。然后我可以自己用MVC编写代码。

编辑

好的,所以我认为我发现了盲点的一部分。首先,我将“ AuthorizeEndpoint”标记为“不是这个SO问题的重点”,但这是一个很大的错误。

当我像这样修改AuthorizeEndpoint时:

redirect_uri

如果我像这样修改 [HttpGet] [ImportModelStateFromTempData] public ActionResult Authorize(string clientId, string returnUrl, string responseType) { AuthorizeViewModel viewModel = new AuthorizeViewModel(); ... ... ... return View(viewModel); } [HttpPost] [ExportModelStateToTempData] public async Task<ActionResult> Authorize(AuthorizeViewModel viewModel) { // NOTE: This is in MVC and is postback from *.cshtml View. OAuth2Client.?????? // <=== How to obtain authorization code from WebAPI? ... return Redirect(returnUrl); } 的实现,则:

Create

使用查询参数 public override Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context) { System.Security.Claims.ClaimsIdentity ci = new System.Security.Claims.ClaimsIdentity("Bearer"); context.OwinContext.Authentication.SignIn(ci); context.RequestCompleted(); return Task.FromResult<object>(null); } 将对AuthorizationCodeProvider.Create的任何调用都重定向到 public void Create(AuthenticationTokenCreateContext context) { context.Ticket.Properties.IssuedUtc = DateTime.UtcNow; context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddSeconds(60); // Some random Guid context.SetToken(Guid.NewGuid().ToString("n")); } ! :D

很显然,此实现不在应有的位置,因此我的问题尚未解决。剩余的问题:

  • 现在,任何人都可以请求授权码,client_id被忽略。 /authorize显然没有被打为redirect_uri的一部分。如何在AuthorizeEndpoint中获取ClientId?
  • 授权代码未耦合到客户端。任何拦截代码的人都可以使用它。如何获取code=<THE_RANDOM_GUID>中的ClientId,以便将其与代码一起存储?
  • 授权代码根本没有耦合到用户,它是一个空的ClaimsIdentity。如何在介于两者之间并在AuthorizeEndpoint中获得用户登录页面,以获取登录用户的ValidateClientAuthentication

1 个答案:

答案 0 :(得分:0)

因此,在网上进行了大量搜索之后,我通过搜索github获得了一些成功。显然,OAuthAuthorizationServerProvider提供了AuthorizeEndpoint,并且该方法应同时用于“嘿,您未被授权,请登录!”以及“啊,好极了,这是授权码”。我曾预期OAuthAuthorizationServerProvider可以有两种单独的方法,但事实并非如此。这就解释了为什么在github上,我找到一些以特殊方式实现AuthorizeEndpoint的项目。我已经采纳了。这是一个示例:

public override async Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
{
    if (context.Request.User != null && context.Request.User.Identity.IsAuthenticated)
    {
        var redirectUri = context.Request.Query["redirect_uri"];
        var clientId = context.Request.Query["client_id"];

        var authorizeCodeContext = new AuthenticationTokenCreateContext(
            context.OwinContext, 
            context.Options.AuthorizationCodeFormat,
            new AuthenticationTicket(
                (ClaimsIdentity)context.Request.User.Identity,
                new AuthenticationProperties(new Dictionary<string, string>
                {
                    {"client_id", clientId},
                    {"redirect_uri", redirectUri}
                })
            {
                IssuedUtc = DateTimeOffset.UtcNow,
                ExpiresUtc = DateTimeOffset.UtcNow.Add(context.Options.AuthorizationCodeExpireTimeSpan)
            }));

        await context.Options.AuthorizationCodeProvider.CreateAsync(authorizeCodeContext);

        context.Response.Redirect(redirectUri + "?code=" + Uri.EscapeDataString(authorizeCodeContext.Token));
    }
    else
    {
        context.Response.Redirect("/account/login?returnUrl=" + Uri.EscapeDataString(context.Request.Uri.ToString()));
    }
    context.RequestCompleted();
}

来源:https://github.com/wj60387/WebApiOAUthBase/blob/master/OwinWebApiBase/WebApiOwinBase/Providers/OAuthServerProvider.cs

关于我剩下的三个问题:

  1. 现在,任何人都可以请求授权码,client_id被忽略。 ValidateClientAuthentication显然未作为AuthorizeEndpoint的一部分命中。如何在AuthorizeEndpoint中获取ClientId?

答案:您必须实现“ ValidateClientAuthentication”。

  1. 授权代码未耦合到客户端。任何拦截代码的人都可以使用它。如何在AuthorizationCodeProvider.Create中获取ClientId,以便可以将其与代码一起存储?

答案:OAuthAuthorizationServerProvider会处理这个问题。只要您在票证中设置“ client_id”,它就会检查请求授权码访问令牌的客户端是否相同。

  1. 授权代码根本没有耦合到用户,它是一个空的ClaimsIdentity。如何在两者之间以及在AuthorizeEndpoint中放置用户登录页面,以获取已登录用户的ClaimsIdentity?

回答:您将创建一个单独的登录页面。这样做是使用户登录。如果您的WebAPI使用基于cookie的身份验证,则可以再次将用户重定向到AuthorizeEndpoint。如果您使用访问令牌,则您的登录页面必须使用访问令牌向“ AuthorizeEndpoint”发出请求,以获取授权码。 (不要将访问令牌提供给第三方。您的登录页面会请求授权码并将其发送回去。)换句话说,如果您使用访问令牌,那么此流程中将涉及两个客户端。