3

I am creating a React calendar that take data from "Microsoft Outlook Calendar" using the client-side JavaScript SDK "hello.js" and Microsoft Graph (for the set up I also followed this guide: https://learn.microsoft.com/en-us/graph/auth-register-app-v2).

Using hello.login my app shows the calendar without any problem...but unfortunately I have to show it without a login session.

This is my code:

class CalendarView extends Component {
  constructor(props) {
    super(props);

    hello.init({
      microsoft: {
        id: APP_ID,
        oauth: {
          version: 2,
          auth: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
        },
        scope_delim: ' ',
        form: false,
        scope: SCOPES,
      },
    });

    const { startDate, endDate } = this.props;

    this.state = {
      // events: [],
      startDataTime: startDate.toISOString(),
      endDataTime: endDate.toISOString(),
      token: hello('microsoft').getAuthResponse().access_token,
    };
  }

In this other component I mange the Microsoft Graph Query:

class EventsList extends Component {
  constructor() {
    super();
    this.state = {
      events: [],
    };
  }

  componentWillReceiveProps(nextProps) {
    const { startDate, endDate, token } = nextProps;

    // to know what is the Bearer toke
    // -> https://stackoverflow.com/questions/25838183/what-is-the-oauth-2-0-bearer-token-exactly
    axios.get(
      `https://graph.microsoft.com/v1.0/me/calendarview?startdatetime=${startDate}&enddatetime=${endDate}&orderby=start/dateTime`,
      { headers: { Authorization: `Bearer ${token}` } },
    ).then(response => this.setState({ events: response.data.value }))
      .catch((error) => {
        console.log(error.response);
      });
  }

  render() {
    const { events } = this.state;
    if (events !== null) return events.map(event => <EventList key={event.id} event={event} />);
    return null;
  }
}

The strange thing is that if I make a console.log(token) the app show me the token but, at the same time, I receive an "GET...401 (Unauthorized)" error

console log token and error message

That are my app propriety:

app propriety part 1

app propriety part 2

Maybe the problem is the Hello.js call? I am testing my app with Jest and I have this error, can it be linked to my problem?

 console.error node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/virtual-console.js:29
    Error: Uncaught [TypeError: hello is not a function]

How Can I solve?

TheOldBlackbeard
  • 395
  • 4
  • 22

1 Answers1

2

I found the solution!

I had to make 2 axios call:

  • one to obtain the token (with a POST)
  • one for use the token in my microsoft graph query (with a GET)

I had to register my app here https://portal.azure.com/#home so to obtain a Client ID and Secret.

After I needed to send a POST message to Azure Active Directory Authentication endpoint with following body parameters:

  • grant_type: The flow we want to use, client_credentials in my case.
  • client_id: The Client ID (Application ID) of the application I
    created in the registration step;
  • client_secret: The Client Secret I created in the registration step;
  • resource: The name of the resource I would like to get access, https://graph.microsoft.com in this case.

So I created one component with the following axios POST request:

componentDidMount() {
    axios.post(`https://cors-anywhere.herokuapp.com/https://login.microsoftonline.com/${AZURE_ACTIVE_DIRECTORY_TENANT_NAME}/oauth2/token`,
      `grant_type=${GRANT_TYPE}&client_id=${APP_ID}&client_secret=${SECRET}&resource=${RESOURCE}`).then(res => this.setAccessToken(res.data.access_token))
      .catch((error) => {
        console.error(error.response);
      });
  }

  setAccessToken(token) {
    if (typeof token === 'string') this.setState({ accessToken: token });
  }

NOTE the resource value needed to be a bit changed to work: https%3A%2F%2Fgraph.microsoft.com%2F

I had to put the string 'https://cors-anywhere.herokuapp.com' before micorsoftonline URL because otherwise the application generated

"a blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource." (I don't know why, I am still working on it because putting this string before is not an optimal solution).

In EventList component I didn't need hellojs anymore, so I just use the token I generated to access. I had to change just a bit the microsoft graph query:

 componentDidMount() {
    const { accessToken } = this.props;
    const { startDate, endDate } = this.state;
    this.getEvents(startDate, endDate, accessToken);
  }

getEvents(startDate, endDate, accessToken) {
    const startDateString = startDate.toISOString();
    const endDateString = endDate.toISOString();
    axios.get(
      `https://graph.microsoft.com/v1.0/users/${USER_PUBLIC_ID}/calendarview?startdatetime=${startDateString}&enddatetime=${endDateString}&orderby=start/dateTime`,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      },
    ).then(response => this.setEvents(response.data.value))
      .catch((error) => {
        console.error(error.response);
      });
  }

  setEvents(events) {
    const validEvent = event => typeof event.subject === 'string';
    this.setState({ events: events.filter(validEvent) });
  }

I hope that my solution can be usefull also to other users

TheOldBlackbeard
  • 395
  • 4
  • 22
  • Thanks for posting your approach. A few questions about your app: 1. Are you using node.js backend to get the tokens? 2. Are you validating the tokens obtained before sending to MS Graph? 3. My understanding is you are using client_credentials grant type since you do not have a user to login. Right? Please do check out the [ADAL node](https://github.com/AzureAD/azure-activedirectory-library-for-nodejs#server-to-server-via-client-credentials) library which handles the token request and validation. – Navya Canumalla Jan 23 '19 at 02:22
  • Hello. To get the token I built a component in my react application in which, in componentDidMount() i made an Axios Post call (I used Axios to Fetch Micrososft API, so the token) and in componentDidMount() i made also the "setState" (I just check with the function setAccessToken if the value that I got was a string). Exactly, I used APP_ID, SECRET, RESOURCE, GRANT_TYPE, AZURE_ACTIVE_DIRECTORY_TENANT_NAME, because I had to show the events of the calendar without a login session. I hope I made u understand what I did, sorry I am still a newbie in React world. – TheOldBlackbeard Jan 25 '19 at 08:57
  • 1
    I just leave it here: https://www.npmjs.com/package/react-microsoft-login – Alexandr Tovmach Dec 01 '19 at 14:49