Web Api 到 Signalr Azure 令牌无法正常工作

Web Api to Signalr Azure token not working

我有一个 Asp.Net Web Api (Api1) 需要向 Signalr Realtime Api (Api2) 发送消息.我正在尝试使用 Azure AD 不记名令牌进行身份验证。 Api1 的客户端是一个 JavaScript 客户端,它使用 ADAL.js 从 Azure 获取令牌。

    var authContext = new AuthenticationContext({    
            tenant: tenantId,
            clientId: jsclientId,
            postLogoutRedirectUri: window.location.origin,
            cacheLocation: 'localStorage',
            endpoints: {
                api1Url: api1ResourceUri
            }
    });

    authContext.acquireToken(jsclientId, function (error, token) {
        if (error || !token) {
            authContext.clearCache();
            authContext.login();
        } 
    });

JS 客户端在授权 header 中将此令牌附加到对 Api1 的所有 Api 调用。在 Api1 中,我使用以下代码从 Azure AD 获取访问令牌。

var userAssertion = new UserAssertion(bootstrapContext.Token, "urn:ietf:params:oauth:grant-type:jwt-bearer", userName);

var result = await authenticationContext.AcquireTokenAsync(api2ResourceId,  new ClientCredential(api1clientId, api1clientSecret), userAssertion);

我将此访问令牌作为授权附加到请求中 header "Bearer tokenvalue"。在 Signalr Hub Owin Startup class 我有以下代码。

app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
    TokenValidationParameters = new TokenValidationParameters
    {
            ValidAudiences = api1Audiences,
            SaveSigninToken = true
    },
    Tenant = configSection.TenantId
});

虽然中心上的 ClaimsIdentity 显示为已通过身份验证,但未设置用户身份。 identity.name 为空。看起来用户身份没有传递到 Signalr 中心。

您在 API 中没有获得用户身份,因为您是作为应用程序而不是用户对其进行身份验证。

仅获取资源和 ClientCredentials 的 acquireTokenAsync 重载用于客户端凭据流(a.k.a 仅限应用程序流)。

您需要做的是使用 On-behalf-of 流程将您为 API1 获得的令牌交换为 API2.

的令牌

所以在 API1 的 Startup.Auth,TokenValidation 参数中,将 Save SigninToken 设置为 true,如下所示:

app.UseOpenIdConnectAuthentication(
  OpenIdConnectAuthenticationOptions
  {
      // ...
      TokenValidationParameters = new TokenValidationParameters
      {
        SaveSigninToken = true
      },
      // ...
  });

然后无论你想在哪里调用你的 API2,请执行以下操作:

ClientCredential clientCred = new ClientCredential(clientId, appKey);
var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext as System.IdentityModel.Tokens.BootstrapContext;
string userName = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn) != null ? ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn).Value : ClaimsPrincipal.Current.FindFirst(ClaimTypes.Email).Value;
string userAccessToken = bootstrapContext.Token;
UserAssertion userAssertion = new UserAssertion(bootstrapContext.Token, "urn:ietf:params:oauth:grant-type:jwt-bearer", userName);

result = await authContext.AcquireTokenAsync(api2ResourceId, clientCred, userAssertion);

在示例中查看:https://github.com/Azure-Samples/active-directory-dotnet-webapi-onbehalfof,特别是 ToDoService 的 Startup.Auth.cs 和 TodoListController.cs.

注意:此示例适用于本机应用程序 + 网络 api,您正在将其调整为网络应用程序 + 网络 api.

Edit - 通过在 JS acquireToken 调用中指定 API1 来确保您的 JS 代码正在为您的 API1 请求令牌,如下所示:

var authContext = new AuthenticationContext({    
        tenant: tenantId,
        clientId: jsclientId,
        postLogoutRedirectUri: window.location.origin,
        cacheLocation: 'localStorage',
        endpoints: {
            api1Url: api1ResourceUri
        }
});

authContext.acquireToken(api1clientId, function (error, token) {
    if (error || !token) {
        authContext.clearCache();
        authContext.login();
    } 
});