ASP.NET Web API和OpenID Connect:如何从授权代码获取访问令牌

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

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

看起来推荐的方法是使用AuthorizationCodeReceived事件来交换访问令牌的Auth代码。 Vittorio有一个博客文章 ,概述了整体流程。

以下是来自此示例应用程序的示例应用程序在GitHub上的Startup.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事件。 如果已生成并存储了auth代码,则不会调用此事件。 您必须注销或清除cookie才能强制执行此事件。

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将很快过期。 存储它没有任何意义。
  • 第二个问题是,只要authorizationCode未过期并存储在会话中, AuthorizationCodeReceived事件就不会被任何页面重新加载触发。

你需要做的是调用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)。 如果令牌已过期,则会引发AdalSilentTokenAcquisitionExceptionexception(调用访问代码续订过程)。

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

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

您需要绕过默认的owinvalidation来执行自定义授权:

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