我有一个React Web应用程序,该应用程序需要访问Azure中托管的ASP.NET Core API。这两个应用程序均受Azure AD保护。我已经在Azure AD应用程序注册中分别注册了这些应用程序。我已经公开了给我这个范围的API:api:// {api_azure_client_id} / user_impersonation-此范围允许用户和管理员双方同意。我分别注册了react应用,并授予它访问公开的API范围的权限。
我正在使用MSAL来获取令牌。我可以成功签名,但是我不知道如何获取access_token来调用API。我正在关注the azure ad samples for react here。使用钩子的决定是受this pull request on the sample.
的启发MSAL配置:
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挂钩:
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,它只是axios的包装。我正在使用此服务来调用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中从那里获取它。根据上面的代码,在哪里可以访问令牌并将其保存在本地存储中?
答案 0 :(得分:0)
我可以成功签名,但是我不知道如何获得 access_token调用API。
您可以成功签名,您已经在本地存储中有一个缓存的令牌(cacheLocation:“ localStorage”)。如果令牌尚未过期,则可以静默获取。
var accessToken = await this.userAgentApplication.acquireTokenSilent({
scopes: config.scopes
});
如果您要访问自己的webapi,则范围应该类似于api://{api_azure_client_id}/user_impersonation
。
答案 1 :(得分:0)
在Sign方法中,您可以通过添加window.localStorage.setItem('accessToken',tokenResponse.accessToken);
将访问令牌保存到本地存储中。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);
window.localStorage.setItem('accessToken',tokenResponse.accessToken);
const graphProfile = await fetchMsGraph(
GRAPH_ENDPOINTS.ME,
tokenResponse.accessToken
).catch(() => {
setError("Unable to fetch Graph profile.");
});
if (graphProfile) {
setGraphProfile(graphProfile);
}
}
}
}
在httpService中从本地存储中获取令牌,然后添加window.localStorage.getItem('accessToken');然后将其传递给Axios标头