Вызов защищенной функции Azure в Azure AD из React SPA/ статического веб-приложения Azure
Я разрабатываю SPA с React, который размещается как статическое веб-приложение Azure. Приложение защищено аутентификацией Azure AD, которая отлично работает, я уже создал логин, который отлично работает, и я могу вызывать API-интерфейсы Azure (Graph) с полученным токеном и извлекать информацию для предоставленных областей (например, изображение профиля пользователя). Для этого я использую оболочку React AAD MSAL, которая аккуратно обертывает библиотеку аутентификации Microsoft (msal@1.4.0).
Пока все хорошо, никаких проблем. Но мне, конечно, нужен бэкэнд. Я решил сделать это с помощью функций Azure, поскольку бессерверный вариант для меня здесь лучший вариант. Итак, я сделал быстрый прототип триггера HTTP, который работает в Azure как функция Azure и работает, когда я вызываю URL-адрес с правильными параметрами.
Но, конечно, функция Azure должна быть защищена, поэтому только мое приложение React может вызывать эту функцию. Поэтому я подумал, что должен быть способ сделать это через Azure AD, поскольку мой пользователь уже вошел в систему как таковой.
Я пробовал и пробовал и пробовал разные способы, которые нашел в Интернете, но ни один из них не работает, или я делаю что-то не так.
Общий учебник, которому я пытался следовать, принадлежит самой MS. Я пробовал использовать настройку "Экспресс", которая, конечно, не сработала. Я попробовал расширенную конфигурацию, которая тоже не сработала. В расширенном руководстве говорится, что вам нужно иметь регистрацию приложения для службы, я даже не уверен, может ли это быть мое статическое веб-приложение или новое приложение (я пробовал и то, и другое безуспешно). Разве недостаточно сообщить функции Azure, что теперь она защищена AAD и может принимать вызовы только из источника, защищенного токеном доступа, который содержит идентификатор приложения моего приложения, который указан в настройках? Вы можете легко предоставить все эти настройки, это просто не работает.
Так что я очень рано задерживаюсь здесь. Чтобы вызвать саму функцию, мне сначала нужно получить токен авторизации. Согласно этому руководству от MS (см. "Проверка токенов от поставщиков"), мне нужно отправить токен доступа, который я получил при входе в свое веб-приложение SPA, в конечную точку функции Azure, заканчивающуюся на
.auth/login/aad
. Получить этот токен легко, поскольку React AAD MSAL предоставляет метод
authProvider.getAccessToken()
который я могу использовать для его извлечения. Затем я отправляю запрос POST на
https://<My Azure Function URI>/.auth/login/aad
с токеном доступа в теле как JSON
{ 'access_token': authToken.accessToken }
. Я должен получить токен аутентификации, который затем могу использовать для вызова фактической функции, но я всегда получаю один и тот же ответ, независимо от того, что я пытаюсь:
You do not have permission to view this directory or page.
Так вот где я. Я пробовал разные методы и решения, но безрезультатно. Может быть, я сделал что-то не так с нуля, возможно, я использую неправильные методы, я действительно не знаю на данный момент. У кого-нибудь есть опыт с этим? Что-то не так в моем общем подходе, нужно ли мне что-то делать? Или мне просто нужно что-то изменить в конфигурации? Любая помощь здесь будет принята с благодарностью.
Изменить: поскольку его спросили, вот как я получаю токен. Концепция, лежащая в основе этого, заключается в использовании
redux-thunk
для отправки асинхронного действия в хранилище response-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, которые важны для этого (если я что-то забыл, напишите мне).
Функция Azure:
Аутентификация активирована, только AAD auth, дополнительные настройки:
Функция Azure - Регистрация приложения:
Предоставление API - предоставление API user_impersonation, чтобы веб-приложение могло его использовать:
Статическое веб-приложение Azure (React SPA) - Регистрация приложения:
Идентификатор URI приложения, который используется как Token Audience в функции Azure (расширенная настройка проверки подлинности):
Разрешения API - с использованием API user_impersonation, который предоставляется при регистрации приложения-функции Azure:
Если в этой конфигурации что-то не так, сообщите мне. Скорее всего, это так, но я не знаю, что, поскольку я следил за учебником на MSDN. Я только потом добавил user_impersonation, потому что он не работал.
2 ответа
Согласно предоставленной информации, вы не настраиваете правильную область в своем
authProvider
файл. Вам необходимо добавить область, которую вы определяете при создании приложения AD для защиты функции. Поэтому, пожалуйста, обновите объем как
scopes: ["openid","<your function app scope>"]
в
authProvider
.
Например
Создание приложения Azure AD для защиты функции
Зарегистрируйте приложение Azure AD. После того, как сделать это, скопировав Application (клиент) ID и Directory (арендатор) ID
Настройте URI перенаправления. Выберите Интернет и введите
<app-url>/.auth/login/aad/callback
.Включить неявный поток грантов
Создайте секрет клиента.
Включение Azure Active Directory в приложении службы приложений
Создайте клиентское приложение AD для доступа к функции
- Зарегистрировать заявку
- Включить неявный поток грантов
- настроить разрешения API. Позвольте вашему клиентскому приложению иметь разрешения на доступ к функции
Код
-
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",
}
);
- Вызов API
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);
};
Некоторое время я занимался той же проблемой. Если вы уверены, что получаете правильный токен доступа и правильно его передаете, то просмотрите конфигурацию на портале. Если вы автоматически создали регистрацию приложения для приложения-функции, проверьте, как настроен URL-адрес ISSUER. Вы можете найти это в приложении-функции> аутентификация> редактировать. убедитесь, что в конце URL-адреса нет /v2.0. Функция Azure работает только с маршрутом по умолчанию (/v1.0).