2

我正在开发一个带有 React 的 SPA,它作为 Azure 静态 Web 应用程序托管。该应用程序受到 Azure AD 身份验证的保护,效果很好,我已经构建了一个运行良好的登录,我可以使用我获得的令牌调用 Azure (Graph) API 并检索授予范围的信息(例如用户个人资料图片)。为此,我使用了一个名为React AAD MSAL的包装器,它巧妙地包装了 Microsoft 身份验证库 (msal@1.4.0)。

到目前为止一切顺利,这里没有问题。但我当然需要一个后端。我决定使用 Azure Functions,因为无服务器对我来说是最好的方式。因此,我制作了一个快速 HTTP 触发原型,该原型在 Azure 中运行,当我使用正确的参数调用 URL 时,Azure Function und 可以正常工作。

但是当然 Azure Function 需要被保护,所以只有我的 React App 可以调用这个函数。所以我认为应该有办法通过 Azure AD 来做到这一点,因为我的用户已经这样登录了。

我尝试并尝试了我在网上找到的不同方法,但它们似乎都不起作用,或者我做错了什么。

我尝试遵循的一般教程是来自MS 本身的教程。我尝试使用“Express”设置,这当然不起作用。我尝试了高级配置,但也没有用。高级教程说您需要为该服务注册一个应用程序,我什至不确定这是否可以是我的静态 Web 应用程序或新的(我都尝试过但没有成功)。告诉 Azure 函数它现在受 AAD 保护并且只能接受来自由包含我的应用程序的应用程序 ID(在设置中提供)的访问令牌保护的源的调用还不够吗?您可以轻松提供所有这些设置,但它似乎不起作用。

所以我很早就在这里停滞不前。要调用函数本身,我首先需要获取一个授权令牌。根据MS 的本教程(请参阅“验证来自提供商的令牌”),我需要将登录到我的 SPA Web 应用程序时获得的访问令牌发送到以.auth/login/aad. 获取这个令牌很容易,因为 React AAD MSAL 提供了一种authProvider.getAccessToken()我可以用来提取它的方法。https://<My Azure Function URI>/.auth/login/aad然后,我使用正文中的访问令牌作为 JSON发出 POST 请求{ 'access_token': authToken.accessToken }。我应该得到一个身份验证令牌,然后我可以用它来调用实际的函数,但无论我尝试什么,我总是得到相同的响应:You do not have permission to view this directory or page.

所以这就是我所在的地方。我尝试了不同的方法和解决方案,但发现无济于事。也许我从头开始做错了什么,也许我使用了错误的方法,我现在真的不知道。这个事情谁有经验?我的一般方法有问题吗,我需要做其他事情吗?还是只是我需要更改的配置中的某些内容?

编辑

既然有人问了,这就是我检索令牌的方式。这背后的概念是使用 aredux-thunk将异步操作分派到 react-redux 存储。我不仅在这里简化了这个问题,还简化了我的测试。现在我只是想获取身份验证令牌并记录 POST 请求给我的答案:

import { authProvider } from '../../Authentication/AuthProvider';

//Fetch
async function getAccessToken(authToken) {
  const body = { 'access_token': authToken.accessToken };

  fetch('https://<My Azure function URL>/.auth/login/aad', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(body)
    },
  ).then(response => {
    console.log(response);
  });
}

export const fetchAddressData = () => async dispatch => {
  const token = await authProvider.getAccessToken();
  await getAccessToken(token);
  // The actual call to the Azure function will go here once we have an Authentication Token
}

authProvider是一个组件,react-aad msal配置如下所示:

import { MsalAuthProvider, LoginType } from 'react-aad-msal';

//MSAL Config
const config = {
  auth: {
    authority: '<Tenant ID>',
    clientId: '<Client ID from App registration (Azure Static Web App)>',
    redirectUri: window.location.origin
  },
  cache: {
    cacheLocation: "localStorage",
    storeAuthStateInCookie: true
  }
};

// Authentication Parameters
const authenticationParameters = {
  scopes: [
   'openid',
   'user.read', 
   'https://<Azure Function URI>/user_impersonation'
  ],
  forceRefresh: true
}

// Options
const options = {
  loginType: LoginType.Redirect,
  tokenRefreshUri: window.location.origin
}

export const authProvider = new MsalAuthProvider(config, authenticationParameters, options)

编辑 2

我调整了一些额外的设置,试图与用户模拟一起工作,但仍然没有成功。下面是对我当前重要的 Azure 设置的概述(我忘记了吗?)。

蔚蓝功能:

身份验证已激活,仅 AAD 身份验证,高级设置: 在此处输入图像描述

Azure 功能 - 应用注册:

身份验证设置: 在此处输入图像描述

客户端密码: 在此处输入图像描述

公开 API - 公开 user_impersonation API,以便 Web 应用可以使用它: 在此处输入图像描述

Azure 静态 Web 应用 (React SPA) - 应用注册:

在 Azure 函数中用作令牌受众的应用程序 URI ID(高级身份验证设置): 在此处输入图像描述

API 权限 - 使用 Azure Function App Registration 公开的 user_impersonation API:

在此处输入图像描述

这个配置有什么问题吗?很可能是,但我不知道是什么,因为我按照 MSDN 上的教程进行操作。之后我只添加了 user_impersonation,因为它不起作用。

4

2 回答 2

3

根据提供的信息,您没有在authProvider文件中配置正确的范围。您需要在创建 AD 应用程序时添加您定义的范围以保护功能。所以请更新范围,如scopes: ["openid","<your function app scope>"].authProvider

例如

  • 创建 Azure AD 应用程序以保护功能

    1. 注册 Azure AD 应用程序。之后,请复制 应用程序(客户端)ID目录(租户)ID

    2. 配置重定向 URI。选择Web并键入<app-url>/.auth/login/aad/callback

    3. 启用隐式授权流

    4. 定义 API 范围并复制它 在此处输入图像描述

    5. 创建客户端密码。

  • 在应用服务应用中启用 Azure Active Directory

  • 创建客户端 AD 应用程序以访问功能

    1. 注册申请
    2. 启用 隐式授权流
    3. 配置 API 权限。让您的客户端应用程序具有访问功能的权限
  • 代码

    1. authProvider
 import { MsalAuthProvider, LoginType } from "react-aad-msal";
import { Logger, LogLevel } from "msal";

export const authProvider = new MsalAuthProvider(
  {
    auth: {
      authority: "https://login.microsoftonline.com/<tenant>",
      clientId: "<>",
      postLogoutRedirectUri: window.location.origin,
      redirectUri: window.location.origin,
      validateAuthority: true,
      navigateToLoginRequestUrl: false,
    },
    system: {
      logger: new Logger(
        (logLevel, message, containsPii) => {
          console.log("[MSAL]", message);
        },
        {
          level: LogLevel.Verbose,
          piiLoggingEnabled: false,
        }
      ),
    },
    cache: {
      cacheLocation: "sessionStorage",
      storeAuthStateInCookie: true,
    },
  },
  {
    scopes: [
      "openid",
      "<the scope you define for your function>",
    ],
    forceRefresh: true,
  },
  {
    loginType: LoginType.Popup,
    tokenRefreshUri: window.location.origin + "/auth.html",
  }
);


  1. 调用接口
const CallAPI= async () => {
    // You should should use getAccessToken() to fetch a fresh token before making API calls
    const authToken = await provider.getAccessToken();
    console.log(authToken.accessToken);
    let body = { access_token: authToken.accessToken };

    let res = await fetch(
      "<your function url>/.auth/login/aad",
      {
        method: "POST",
        mode: "cors",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(body),
      }
    );

    let data = await res.json();
    console.log(data);
    body = { name: "Azure" };
    res = await fetch("<>", {
      method: "POST",
      mode: "cors",
      headers: {
        "Content-Type": "application/json",
        "X-ZUMO-AUTH": data["authenticationToken"],
      },
      body: JSON.stringify(body),
    });
    data = await res.text();
    console.log(data);
  };

在此处输入图像描述

于 2020-09-29T03:32:58.957 回答
0

我有一段时间在处理同样的问题。如果您确定您获得了正确的访问令牌并正确传递了它,那么请查看门户中的配置。如果您为函数应用自动创建了应用注册,请检查 ISSUER URL 的设置方式。您可以在功能 app>authentication>edit 中找到它。确保 url 最后没有 /v2.0 。Azure 函数仅适用于默认 (/v1.0) 路由。

于 2021-04-15T23:18:24.180 回答