【问题标题】:ASP.NET Web API and OpenID Connect: how to get Access Token from Authorization CodeASP.NET Web API 和 OpenID Connect:如何从授权码中获取访问令牌
【发布时间】:2023-03-30 21:32:02
【问题描述】:

我试图让 OpenID Connect 运行...我的 Web API 用户设法获得了 OpenID Connect 提供者的授权码。我应该如何将此代码传递给我的 ASP.NET Web API?如何配置 OWIN 中间件,以便我可以使用授权码获取访问令牌?

更新: SPA 使用 AJAX 与我的 Web 服务 (ASP.NET Web API) 进行通信。在我的网络服务中使用 OWIN 中间件。我将 OpenIDConnect 设置为身份验证机制。第一次调用 Web 服务时,它成功地将用户重定向到 OpenID Connect Provider 的登录页面。结果,用户可以登录并获得授权码。 AFAIK 现在可以(由我的 Web 服务)将此代码用于访问令牌。但是,我不知道如何将此代码返回到我的 Web 服务(这是使用标头完成的吗?),然后如何配置以获取访问令牌。我想我可以手动调用令牌端点,但我想改用 OWIN 组件。

【问题讨论】:

  • 我不清楚你想要完成什么。您是在询问 Web API 的 OWIN 中间件吗?还是某个调用 API 的客户端应用?
  • 好的,我刚刚更新了我的问题。

标签: c# azure asp.net-web-api owin openid-connect


【解决方案1】:

看起来推荐的方法是使用AuthorizationCodeReceived 事件将身份验证代码交换为访问令牌。 Vittorio has a blog entry 概述了整个流程。

这是一个来自this sample app on GitHubStartup.Auth.cs 代码示例:

app.UseOpenIdConnectAuthentication(
    new OpenIdConnectAuthenticationOptions
    {
        ClientId = clientId,
        Authority = Authority,
        Notifications = new OpenIdConnectAuthenticationNotifications()
        {
            AuthorizationCodeReceived = (context) =>
           {
               var code = context.Code;
               ClientCredential credential = new ClientCredential(clientId, appKey);
               string tenantID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
               string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
               AuthenticationContext authContext = new AuthenticationContext(string.Format("https://login.windows.net/{0}", tenantID), new EFADALTokenCache(signedInUserID));
               AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
                           code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceID);

               return Task.FromResult(0);
            },
            ...
    }

注意:当授权真正发生时,AuthorizationCodeReceived 事件只被调用一次。如果已经生成并存储了验证码,则不会调用此事件。您必须注销或清除 cookie 才能强制执行此事件。

【讨论】:

  • 谢谢,有道理。但是,在我的情况下,AuthorizationCodeReceived 永远不会触发......我想我必须以某种方式将授权码从 SPA 传递到网络服务......
【解决方案2】:

BenV 已经回答了这个问题,但还有更多需要考虑。

class partial Startup
{
    public void ConfigureAuth(IAppBuilder app)
    {
        // ...

        app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
          {
            ClientId = clientId,
            Authority = authority,
            Notifications = new OpenIdConnectAuthenticationNotifications() {
                AuthorizationCodeReceived = (context) => {
                   string authorizationCode = context.Code;
                   // (tricky) the authorizationCode is available here to use, but...
                   return Task.FromResult(0);
                }
            }
          }
    }
}

两个问题:

  • 首先,authorizationCode 会很快过期。存储它没有任何意义。
  • 第二个问题是AuthorizationCodeReceived 事件不会因为任何页面重新加载而被触发,只要 authenticationCode 未过期并存储在会话中。

你需要做的是调用AcquireTokenByAuthorizationCodeAsync,它将缓存它并在TokenCache.DefaultShare中正确处理:

AuthorizationCodeReceived = (context) => {
    string authorizationCode = context.Code;
    AuthenticationResult tokenResult = await context.AcquireTokenByAuthorizationCodeAsync(authorizationCode, new Uri(redirectUri), credential);
    return Task.FromResult(0);
}

现在,在 每次 调用资源之前,调用 AcquireTokenSilentAsync 以获取 accessToken(它将使用 TokenCache 或静默使用 refreshToken )。如果令牌过期,会引发AdalSilentTokenAcquisitionException 异常(调用访问码更新程序)。

// currentUser for ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")
AuthenticationResult authResult = await context.AcquireTokenSilentAsync(resourceUri, credential, currentUser);

如果令牌被缓存,调用AcquireTokenSilentAsync 非常快。

【讨论】:

  • 如果调用AcquireTokenSilentAsync 时抛出异常二重奏authorizationCode 已过期。接下来具体做什么?获取新的授权码?在我的应用程序中,当它发生时,我使用了AcquireToken,它返回了一个错误令牌
  • @HiepLam,您需要先调用 AcquireTokenByAuthorizationCodeAsync(),如示例所示。调用 AcquireTokenSilentAsync() 是为了在请求处理期间进行进一步调用,因此您不存储“授权代码”。但是,也有可能是您没有对该资源的权限,因此例外。
  • 但是它需要通过验证然后认证服务器发回authorization code 对(OnOpenIdAuthorizationCodeReceived OWIN 管道上的事件)?那么,当代码过期时,我如何获得另一个 authorization code
  • @HiepLam,您不必存储authorization code。调用 AcquireTokenByAuthorizationCodeAsync() 会将其放入内部缓存中,进一步调用 AcquireTokenSilentAsync() 在内部使用此缓存(因此您不必自己存储它)。
  • 我理解你的想法。第一次,我在AuthorizationCodeReceived 事件中打电话给AcquireTokenByAuthorizationCodeAsync,一切正常。一段时间后,authorization code 过期,调用AcquireTokenSilentAsync 时抛出异常。所以当时我想知道如何获得一个新的authorization code。或者有其他方法可以解决这个问题吗?
【解决方案3】:

你需要绕过默认的owin验证来做自定义授权:

           new OpenIdConnectAuthenticationOptions
            {
                ...,
                TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
                {
                    ValidateIssuer = false
                },

【讨论】:

    【解决方案4】:
    TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name", RoleClaimType = ClaimTypes.Role },
    

    这行代码解决了我的问题。我们需要验证颁发者是否为假。

    【讨论】:

      猜你喜欢
      • 2017-09-26
      • 2018-11-23
      • 2015-02-24
      • 2021-11-05
      • 2021-12-08
      • 1970-01-01
      • 2018-06-13
      • 2022-08-16
      • 1970-01-01
      相关资源
      最近更新 更多