I am making an application in React and use the "react-aad-msal" library (https://www.npmjs.com/package/react-aad-msal), which is authenticated and authorized using AzureAD and then receives data (list tasks) from API (as I took this example https://github.com/Azure-Samples/ms-identity-javascript-angular-spa-dotnetcore-webapi-roles-groups/tree/master/chapter2/TodoListAPI). I have the following components: authProvider.js, TodoConstants.js, TodoActions.js, TodoReducer.js, TodoContainer.js, TodoSaga.js. When I added const token = await authProvider.getAccessToken () to request data from the API in TodoSaga.js, I started getting an error: Unhandled Rejection (ClientAuthError): User login is required. For silent calls, request must contain either sid or login_hint. If you add loginHint: "myMail@domain.com" to authProvider.js in authenticationParameters, then it starts working correctly and offers to go directly to the authentication stage, but how to specify loginHint or sid correctly is not clear. Please tell me how to get loginHint or sid correctly in this situation? And which parameter is preferable?
authProvider.js
import { MsalAuthProvider, LoginType } from "react-aad-msal";
import { Logger, LogLevel } from "msal";
const config = {
auth: {
clientId: "xxxx-xxxx-xxxx-xxxx",
authority:
"https://login.microsoftonline.com/yyyy-yyyy-yyyy-yyyy",
validateAuthority: true,
redirectUri: "http://localhost:3000",
postLogoutRedirectUri: "http://localhost:3000",
navigateToLoginRequestUrl: true,
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: true,
},
};
const authenticationParameters = {
scopes: [
"openid",
"profile",
"api://iiii-iiii-iiii-iiii/access_as_user",
],
// loginHint: loginHint, <---- How to get it correctly?
// sid: sid, <---- How to get it correctly?
};
const options = {
loginType: LoginType.Popup,
tokenRefreshUri: window.location.origin + "/auth.html",
};
export const authProvider = new MsalAuthProvider(
config,
authenticationParameters,
options
);
TodoConstants.js
export const TODODATASOURCE_API_CALL_REQUEST = "TODODATASOURCE_API_CALL_REQUEST";
export const TODODATASOURCE_API_CALL_SUCCESS = "TODODATASOURCE_API_CALL_SUCCESS";
export const TODODATASOURCE_API_CALL_FAILURE = "TODODATASOURCE_API_CALL_FAILURE";
TodoActions.js
import * as types from "./TodoConstants";
export const fetchTodoDataSourceRequest = () => {
return {
type: types.TODODATASOURCE_API_CALL_REQUEST
};
};
export const fetchTodoDataSourceSuccess = response => {
return {
type: types.TODODATASOURCE_API_CALL_SUCCESS,
data: response,
};
};
export const fetchTodoDataSourceFail = () => {
return {
type: types.TODODATASOURCE_API_CALL_FAILURE
};
};
TodoReducer.js
import * as types from "./TodoConstants";
const initialState = {
fetching: false,
data: null,
error: null,
};
export default function reducer(state = initialState, action) {
switch (action.type) {
case types.TODODATASOURCE_API_CALL_REQUEST:
return { ...state, fetching: true, error: null };
case types.TODODATASOURCE_API_CALL_SUCCESS:
return {
...state,
fetching: false,
data: action.data,
};
case types.TODODATASOURCE_API_CALL_FAILURE:
return {
...state,
fetching: false,
data: null,
error: action.error
};
default:
return state;
}
}
TodoContainer.js
import React from "react";
import TodoView from "../../views/TodoView";
import { connect } from "react-redux";
import * as actions from "./TodoActions";
// import { bindActionCreators } from "redux";
import { bindActionCreators } from "redux";
class TodoDataSourceContainer extends React.PureComponent {
componentDidMount() {
this.props.fetchTodoDataSourceRequest();
}
render() {
return (
<React.Fragment>
<TodoDataSourceView {...this.props} />
</React.Fragment>
);
}
}
const mapStateToProps = ({
TodoDataSourceContainerReducerState: { fetching, data, error},
}) => {
return {
fetching: fetching,
data: data,
error: error,
};
};
const mapDispatchToProps = (dispatch) => {
const {
fetchTodoDataSourceRequest: fetchTodoDataSourceRequest,
} = bindActionCreators(actions, dispatch);
return {
fetchTodoDataSourceRequest: fetchTodoDataSourceRequest,
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(TodoDataSourceContainer);
TodoSaga.js
import { takeLatest, call, put } from "redux-saga/effects";
import * as types from "./TodoConstants";
import * as actions from "./TodoActions";
import { authProvider } from "../../../utils/Auth/authProvider";
const request = async (url) => {
const token = await authProvider.getAccessToken(); // <---- After adding this, an error appears and the need for loginHint or sid
const response = await fetch(url, {
method: "GET",
headers: {
Authorization: "Bearer " + token.accessToken,
"Content-Type": "application/json",
},
});
if (response.ok) {
let result = await response.json();
return result;
} else {
alert("Error HTTP: " + response.status);
}
};
export default function* watcherSaga() {
yield takeLatest(types.TODODATASOURCE_API_CALL_REQUEST, workerSaga);
}
function* workerSaga() {
try {
const response = yield request("https://localhost:44351/api/todolist");
yield put(actions.fetchTodoDataSourceSuccess(response));
} catch (error) {
yield put(actions.fetchTodoDataSourceFail(error));
}
}
