7

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

我试图按照这里的指示https://docs.microsoft.com/en-us/aspnet/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server但在我的实施中,我没有找到了如何到达(我在 中设置的)的Create调用。AuthorizationCodeProviderOAuthAuthorizationServerOptions

我已经简要检查了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);
    }

现在,当我AuthorizeEndpointPath用 a调用 my 时redirect_uri,我会立即被发送到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 来生成代码?

    [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,我也会接受它作为答案。然后我可以自己在MVC中编写代码。

编辑

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

当我像这样调整 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重定向到!:Dredirect_uri code=<THE_RANDOM_GUID>

显然,这个实现不是它应该在的地方,所以我的问题还没有解决。剩下的问题:

  • 现在,任何人都可以请求授权码,client_id 被忽略。ValidateClientAuthentication显然没有作为AuthorizeEndpoint. 如何在 AuthorizeEndpoint 中获取 ClientId?
  • 授权码不耦合到客户端。任何截获代码的人都可以使用它。如何获取 ClientIdAuthorizationCodeProvider.Create以便可以将其与代码一起存储?
  • 授权代码根本不与用户耦合,它是一个空的 ClaimsIdentity。如何在 AuthorizeEndpoint 之间放置一个用户登录页面,并在其中获取ClaimsIdentity已登录用户的登录用户?
4

1 回答 1

3

所以,在网上搜索了一番之后,我通过搜索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”发出请求以获取授权码。(不要将访问令牌提供给第三方。您的登录页面请求授权码并将其发回。)换句话说,如果您使用访问令牌,则此流程涉及两个客户端。

于 2019-05-01T11:42:25.650 回答