ASP.NET Core 6 和 ReactJS Azure 身份验证集成

ASP.NET Core 6 & ReactJS Azure Authentication Integration

提问人:Simon Painter 提问时间:11/3/2023 最后编辑:TylerHSimon Painter 更新时间:11/8/2023 访问量:27

问:

我正在尝试获得一个 ReactJS 网站,以便能够与围绕我们庄园设置的 Web API 进行通信。我们使用 Azure 集成 SSO,我希望能够使用它,以便用户可以使用 React 前端登录,然后使用该登录从我们的 Web API 请求数据。我一直在尝试让它工作,但我无法在 ASP.NET Core 6 控制器操作中获取用户详细信息,但它似乎既不存在于 User 对象中,也不存在于 Request 的“access_token”属性中。

我们在 React 前端使用“@azure/msal-react”对用户进行身份验证。它在那里工作正常 - 用户能够通过 SSO 登录并被 React 应用程序识别。

使用 MSAL,我调用 MsGraph 来获取用户的详细信息:

 instance.acquireTokenSilent({
        ...loginRequest,
        account: accounts[0]
      }).then((response) => {
        callMsGraph(response.accessToken).then(r => {
          const gData: GraphData = {
            givenName: r.givenName,
            surname: r.surname,
            userPrincipalName: r.userPrincipalName,
            id: r.id,
            accessToken: response.accessToken,
            authToken: r.authToken
          };
          props.setGraphData(gData);
        });

得到 后,我这样调用我们的 Web API:accessToken

export async function getAvailableSites(accessToken: string) {
    const url = `${process.env.REACT_APP_baseurl}/Controller/Action`;
    const headers = new Headers();
    const bearer = `Bearer ${accessToken}`;

        headers.append("Authorization", bearer);

    const options = {
        method: "GET",
        headers: headers
    };

    const result = await fetch(url, options);
    const jsonData = await result.json();
    return jsonData;
}

我在 ASP.NET 中配置了身份验证,如下所示:

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
        .AddJwtBearer("Bearer", options =>
                    {
                        options.TokenValidationParameters = new TokenValidationParameters
                        {
                            ValidateIssuer = true,
                            ValidateAudience = true,
                            ValidateIssuerSigningKey = true,
                        };
                    })
        .AddMicrosoftIdentityWebApp(AzureADConfig)
        .EnableTokenAcquisitionToCallDownstreamApi(graphApiScopes)
        .AddMicrosoftGraph(Configuration.GetSection("GraphAPI"))
        .AddInMemoryTokenCaches();

services.AddCors(o =>
            {
                o.AddPolicy("AllowReactJSPolicy", p =>
                {
                    p.WithOrigins("*");
                });
            });

控制器操作标记为“Authorize”,但 access_token 属性中没有任何内容:

    [Authorize]
    public async Task<JsonResult> Action()
    {
        var accessToken = await HttpContext.GetTokenAsync("access_token");
    }

该对象也是空的。User

我可以获取原始的“授权”HTTP标头,并像这样从中获取数据:

        if (!string.IsNullOrWhiteSpace(token))
        {
            var handler = new JwtSecurityTokenHandler();

            var jsonToken = handler.ReadToken(token);
        }

但大多数数据都是加密的。无论如何 - 肯定有一种比 HTTP 标头的原始处理更好的处理方法,不是吗?

我真的需要知道用户是谁,因为它是我稍后执行的方法之一的必要参数。我还想确保用户得到了适当的验证,并且 ASP.NET Core 不只是让每个请求都通过。

如果没有使用SSO的能力,React.js实际上对我们毫无用处。

反应JS ASP.net-core-6.0

评论

0赞 TylerH 11/8/2023
需要明确的是,您上面的代码是否有效?你只是想知道是否有更好的方法?或者,您希望在access_token属性中显示数据,并想知道为什么看不到任何数据?

答:

0赞 Tiny Wang 11/6/2023 #1

我们现在有一个 reactjs 客户端应用程序 + asp.net 核心 webapi 后端应用程序。因此,我们需要将访问令牌传递给 api,然后我们可以获得安全的 API 数据响应,对吧?顺便说一句,我们已经在 react 应用程序中成功调用了图形 api,我们现在只想让 api 控制器获取我们从图形 API 获得的用户配置文件。

因此,首先我们需要知道,访问令牌不包含包含用户数据的声明,在这种方案中,我们用于调用自己的 webapi 的访问令牌与我们用于调用 Graph API 的访问令牌不同。最重要的区别是 .例如,图形 api 的范围可能看起来像,但自定义 webapi 的范围可能看起来像 。scopeUser.Read.Allapi://app_id/api_permission_name

我们首先要做的,就是改变api项目中的配置,首先我们需要改变的是我们应该使用代替。我们这里有一个教程来指导我们如何公开 API 以使用它来保护你的 Web API,以及如何生成正确的访问令牌来调用此 API。我们这里也有官方示例,几天前我也进行了测试,这是我测试中 api 项目内部配置的摘要。AddMicrosoftIdentityWebApiAddMicrosoftIdentityWebApp

在 API 中设置配置后,现在可以生成一个访问令牌,其范围已公开并分配给身份验证应用。现在我们已经能够将 api 和客户端 app 连接起来,这样我们就可以在请求体中传递用户数据,如果我们不想通过请求传递数据,我们也可以在 api 项目内部调用 graph api,但是,我们可以在 api 项目内使用 on-behalf-of flow 来使用访问令牌生成新的访问令牌来调用图形 API。

这是我的代码片段。

StringValues authorizationToken;
HttpContext.Request.Headers.TryGetValue("Authorization", out authorizationToken);
var token = authorizationToken.ToString().Replace("Bearer ","");
var scopes = new[] { "User.Read.All" };
var tenantId = "xxxx";
var clientId = "xxxx";
var clientSecret = "xxxx";
var onBehalfOfCredential = new OnBehalfOfCredential(tenantId, clientId, clientSecret, token);
var tokenRequestContext = new TokenRequestContext(scopes);
var token2 = onBehalfOfCredential.GetTokenAsync(tokenRequestContext, new CancellationToken()).Result.Token;
var graphClient = new GraphServiceClient(onBehalfOfCredential, scopes);
var user = await graphClient.Users.GetAsync();