Вызовите Azure AD защищенный API от React - PullRequest
0 голосов
/ 21 февраля 2020

У меня есть реагирующее веб-приложение, которому требуется доступ к ASP. NET Базовому API, размещенному в Azure. Оба приложения защищены Azure AD. Я зарегистрировал приложения отдельно в Azure AD App регистрации. Я раскрыл API, который дал мне эту область: api: // {api_azure_client_id} / user_impersonation - эта область позволяет как пользователям, так и администраторам дать согласие. Я зарегистрировал приложение реагирования отдельно и дал ему разрешение на доступ к открытой области действия API.

Я использую MSAL для получения токенов. Я могу успешно подписать, но я не знаю, как получить access_token для вызова API. Я следую за azure образцами объявлений для реакции здесь . Решение использовать хуки было вдохновлено этим запросом на выборку для образца.

MSAL config:

export const GRAPH_SCOPES = {
 OPENID: "openid",
 PROFILE: "profile",
 USER_READ: "User.Read",
 API_CALL: "api://{api_azure_client_id}/user_impersonation"
};

       export const GRAPH_ENDPOINTS = {
        ME: "https://graph.microsoft.com/v1.0/me"
      };

   export const GRAPH_REQUESTS = {
     LOGIN: {
      scopes: [
        GRAPH_SCOPES.OPENID,
        GRAPH_SCOPES.PROFILE,
        GRAPH_SCOPES.USER_READ,
        GRAPH_SCOPES.API_CALL
      ]
    }
  };

    export const msalApp = new UserAgentApplication({
      auth: {
        clientId: "react_app_azure_client_id",
        authority: "https://login.microsoftonline.com/common",
        validateAuthority: true,
        postLogoutRedirectUri: "http://localhost:3000",
        navigateToLoginRequestUrl: false
      },
      cache: {
        cacheLocation: "localStorage",
        storeAuthStateInCookie: isIE()
      }
    });

useAuthProvider hook:

    import { useState, useEffect } from "react";
    import { msalApp, requiresInteraction, fetchMsGraph, isIE, GRAPH_ENDPOINTS, GRAPH_REQUESTS
           } from "./../utils/auth-util";

     // If you support IE, our recommendation is that you sign-in using Redirect APIs
    const useRedirectFlow = isIE();
    // const useRedirectFlow = true;

    export default function useAuthProvider() {
     const [account, setAccount] = useState(null);
     const [accessToken, setAccessToken] = useState(null);
     const [error, setError] = useState(null);
     const [graphProfile, setGraphProfile] = useState(null);

      useEffect(() => {
        async function doAuth() {
         msalApp.handleRedirectCallback(error => {
           if (error) {
      const errorMessage = error.errorMessage
        ? error.errorMessage
        : "Unable to acquire access token.";
      // setState works as long as navigateToLoginRequestUrl: false
      setError(errorMessage);
    }
  });

  const account = msalApp.getAccount();

  setAccount(account);

  if (account) {
    const tokenResponse = await acquireToken(
      GRAPH_REQUESTS.LOGIN,
      useRedirectFlow
    );

    if (tokenResponse) {
      setAccessToken(tokenResponse.accessToken);
      const graphProfile = await fetchMsGraph(
        GRAPH_ENDPOINTS.ME,
        tokenResponse.accessToken
      ).catch(() => {
        setError("Unable to fetch Graph profile.");
      });

      if (graphProfile) {
        setGraphProfile(graphProfile);
      }
    }
  }
}

   doAuth();
   }, []);

     async function acquireToken(request, redirect) {
       return msalApp.acquireTokenSilent(request).catch(error => {
         // Call acquireTokenPopup (popup window) in case of acquireTokenSilent failure
         // due to consent or interaction required ONLY
         if (requiresInteraction(error.errorCode)) {
         return redirect ? msalApp.acquireTokenRedirect(request) : 
                msalApp.acquireTokenPopup(request);
         } else {
          console.error("Non-interactive error:", error.errorCode);
        }
      });
    }

      async function onSignIn(redirect) {
       if (redirect) {
         return msalApp.loginRedirect(GRAPH_REQUESTS.LOGIN);
       }

const loginResponse = await msalApp
  .loginPopup(GRAPH_REQUESTS.LOGIN)
  .catch(error => {
    setError(error.message);
  });

if (loginResponse) {
  setAccount(loginResponse.account);
  setError(null);

  const tokenResponse = await acquireToken(GRAPH_REQUESTS.LOGIN).catch(
    error => {
      setError(error.message);
    }
  );

  if (tokenResponse) {
    setAccessToken(tokenResponse.accessToken);
    const graphProfile = await fetchMsGraph(
      GRAPH_ENDPOINTS.ME,
      tokenResponse.accessToken
    ).catch(() => {
      setError("Unable to fetch Graph profile.");
    });

        if (graphProfile) {
           setGraphProfile(graphProfile);
          }
        }
      }
    }

     function onSignOut() {
        return msalApp.logout();
     }

     return {
       account,
       accessToken,
       error,
       graphProfile,
       onSignIn: () => onSignIn(useRedirectFlow),
       onSignOut: () => onSignOut()
     };
    }

Я создал httpService, который является просто оберткой для топора ios. Я использую этот сервис для вызова API, и конфигурация httpService выглядит следующим образом и мне нужен токен доступа. Я попробовал ниже, но я получаю хуки, которые могут быть вызваны только внутри тела функции.

import axios from "axios";
import useAuthProvider from "./useAuthProvider";

   axios.interceptors.request.use(
     config => {
     const { accessToken } = useAuthProvider();

   if (accessToken) {
      config.headers["Authorization"] = "Bearer " + accessToken;
   }        
      return config;
 },
   error => {
     Promise.reject(error);
   }
  );

   export default {
      get: axios.get,
      post: axios.post,
      put: axios.put,
      delete: axios.delete
   };

Если пользователь не вошел в систему, я хочу отобразить компонент с кнопкой Вход в систему, которая запрашивает у пользователя полномочия. Я делаю это в компоненте App, и он отлично работает.

App. js

   import React from "react";
   import useAuthProvider from "./services/useAuthProvider";
   import NavBar from "./components/navigation/navBar";
   import "./App.css";
   import Welcome from "./components/welcome";

   function App() {
    const { account, accessToken, onSignIn } = useAuthProvider();

    if (account === null)
      return <Welcome authButtonMethod={onSignIn.bind(this)} />;
    return <NavBar />;
  }

    export default App;

Я могу работать с токеном в локальном хранилище, а затем получить его оттуда в HTTPService. Основываясь на моем коде выше, где я могу получить доступ к токену и сохранить его в локальном хранилище?

1 Ответ

0 голосов
/ 24 февраля 2020

Я могу успешно подписать, но я не знаю, как получить access_token для вызова API.

Поскольку вы можете успешно подписать, у вас уже есть кэшированный токен в локальном хранилище. (cacheLocation: "localStorage"). Если токен не просрочен, вы можете получить его в режиме без вывода сообщений.

var accessToken = await this.userAgentApplication.acquireTokenSilent({
        scopes: config.scopes
      });

Если вы хотите получить доступ к своему собственному веб-интерфейсу, области действия должны быть примерно такими, как api://{api_azure_client_id}/user_impersonation.

...