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

AuthorizationCodeProvider: Create is never called, how do I generate the authorization code?

我正在设置自己的 OAuth2 服务器。至此,我在实现OAuthAuthorizationServerProvider中成功实现了GrantResourceOwnerCredentials。现在,因为我正在为我们的业务开发应用程序,所以我想实施 OAuth2 授权代码授权。

我已尝试按照此处的说明进行操作 https://docs.microsoft.com/en-us/aspnet/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server 但在我的实现中,我还没有找到如何到达 AuthorizationCodeProviderCreate 调用(我在 OAuthAuthorizationServerOptions).

我已经简要检查了使用(错误的)代码参数访问 TokenEndpointPath 是否有效,并且在调试器中我看到我的 AuthorizationCodeProviderReceive 调用被命中。当然没有成功,因为我发送的代码是'sometestcode'而不是真正的代码,但是代码被命中所以这意味着我在正确的道路上。

这是我目前的情况:

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

现在,当我用 redirect_uri 调用我的 AuthorizeEndpointPath 时,我会立即被发送到 that 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 检索授权代码。之后,我可以将用户重定向回请求授权代码流的原始客户端(应用程序)。

从我的网站API获取授权码,我在Thinktecture的OAuth2Client中看到三个方法:

似乎都不符合我的要求。我该如何继续调用我的 WebAPI 来生成代码?

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

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

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

也在使用 Postman 测试我的 Web API。如果有人可以帮助我在 Web API 2.0 中获得 URL 那个 returns 授权码,我也会接受它作为答案。然后我就可以自己在MVC里面写代码了

编辑

好吧,所以我想我发现了我的盲点部分。首先,我将“AuthorizeEndpoint”标记为 "not the focus of this SO question",但这是一个大错误。

当我像这样调整 AuthorizeEndpoint 时:

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

任何对 /authorize 的调用都将重定向到 redirect_uri ,并带有查询参数 code=<THE_RANDOM_GUID>! :D

很明显,这个实现不是应该的,所以我的问题还没有解决。剩余问题:

所以,在网上搜索了很多之后,我通过搜索 github 获得了一些成功。显然,OAuthAuthorizationServerProvider 提供 AuthorizeEndpoint 并且该方法应该用于 "Hey, you're not authorized, go log in you!" 以及 "Ahh, okay you're cool, here's an authorization code."。我原以为 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 会处理这个问题。只要在ticket中设置"client_id",它就会检查请求授权码的access token的客户端是否相同

  1. 授权码根本没有与用户耦合,它是一个空的 ClaimsIdentity。如何在 AuthorizeEndpoint 和 AuthorizeEndpoint 之间放置一个用户登录页面来获取登录用户的 ClaimsIdentity?

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