How to integrate Google Calendar events using Google OAuth2
Tags: node, googlecloud, api, javascript
Published: 2023-04-11 22:34:08

Google provides an API to connect your SaaS, such as [Workarise](https://workarise.com), with their services, such as Google Calendar, in order to schedule events or Google Meet events. However, in order to do so, you need to prompt your users to grant access to their respective scopes. Here, we’ll show you how to do that.

## Workarise Meetings feature

To sync Workarise with your Google Calendar and obtain a Google Meet link, we developed a calendar using MUI. When you click on a day, a popover will appear with the option to schedule a meeting.

If you haven’t granted Workarise permissions to access your Google Calendar, it will prompt you to do so. This will redirect you to log in with Google and accept to give access only for your calendar. If you accept, you will be redirected to the Workarise app page, which will show that the authorization was successful.

This UX flow is necessary because Google needs to authenticate the user and only grant access when the user authorizes it. For this purpose, we created a little API with Node.js to manage this flow for Workarise, utilizing the Google OAuth2 API library.

## Time to work

To get started, the first step is to create a project on Google Cloud Console. After creating the project, you can navigate to the "APIs & Services" tab by clicking on the hamburger menu or using the search bar. From there, select "Enable APIs and Services." On the left sidebar, you'll find the "Credentials" tab. Click on it and then select "Create Credentials" > "OAuth Client ID" at the top of the page. 

![Image example showing the GCP UI to go to the OAuth Credentials](https://i.imgur.com/1LY5Pbd.png)

Select "Web application" as the Application type. Next, choose a name for your project and add the necessary links to authorize your project to use this client.

![Image example showing the options to add a credential](https://i.imgur.com/9paj8YN.png)

Once the client is created, open it and copy the Client ID and Client Secret. These keys will be necessary for future use.

The next step is to initialize a Node.js API, which can be done by creating a new directory.

```bash
mkdir google-oauth-api && cd google-oauth-api
```

Initialize npm

```bash
npm init -y
```

Change your scripts in the `package.json` for these

```jsx
"scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js"
  },
```

Install dependencies

```bash
npm i cors express googleapis http https morgan nodemon
```

In the root folder, create a `server.js` file

```jsx
import express from 'express';
import morgan from 'morgan';
import cors from 'cors';

const app = express();
const port = process.env.PORT || 8000;

app.use(express.json());

app.use(express.urlencoded({ extended: false }));

app.use(morgan('dev'));

app.use(
  cors({
    origin: ['http://localhost:3000'], // Here your app dev env/prod urls
  })
);

app.use((req, res, next) => {
  // set the CORS policy
  res.header('Access-Control-Allow-Origin', '*');

  // set the CORS headers
  res.header(
    'Access-Control-Allow-Headers',
    'Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers'
  );

  // set the CORS method headers
  if (req.method === 'OPTIONS') {
    res.header('Access-Control-Allow-Methods', 'GET PATCH DELETE POST');
    return res.status(200).json({});
  }

  next();
});

app.listen(port);

console.log(`Server listening on port ${port}`);
```

We will use Express, Morgan, and Cors to open a port to our endpoint and configure the CORS policy. Additionally, we will set the CORS method headers to enable access to our API from our web application.

To get started, navigate to the root directory and create a new directory named **`src/`**. Inside this directory, create two additional directories named **`controllers/`** and **`routes/`**.

Within the **`controllers/`** directory, create a new file named **`googleCalendar.controller.js`**. This file will contain the necessary logic to interface with the Google Calendar API.

```jsx
import { google } from 'googleapis';

const GOOGLE_CLIENT_ID =
  'YOUR_CLIENT_ID';
const GOOGLE_CLIENT_SECRET = 'YOUR_CLIENT_SECRET';

const oAuth2Client = new google.auth.OAuth2(
  GOOGLE_CLIENT_ID,
  GOOGLE_CLIENT_SECRET,
  'http://localhost:8000' // The root of your API
);

const calendar = google.calendar({
  version: 'v3',
  auth: 'AIzaSyAzgIbwnyoa6eeyDpSNk-8V3aEee5ND4Lc',
});

const scopes = ['https://www.googleapis.com/auth/calendar'];
```

In this step, we will generate a login URL, which we will then send to your web application to be used inside an <a/> tag. It is important to set the **`access_type`**
 to **`offline`**in order to obtain a code.

```jsx
export const generateAuthorizationUrl = async (req, res, next) => {
  const authorizationUrl = oAuth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: scopes,
    include_granted_scopes: true,
  });

  res.send({ url: authorizationUrl });
};
```

After the user confirms the scope access, you should redirect them to the root of the API in order to obtain a code in the URL parameters. Next, use the oAuth library to save that code and generate a refresh token. Finally, redirect the user to a specific route of your web app with the refresh token in the parameters. Once there, retrieve the refresh token and store it in your database, associated with the corresponding user.

```jsx
export const redirect = async (req, res, next) => {
  const { code } = req.query;

  try {
    oAuth2Client.getToken(code, (err, tokens) => {
      if (err) {
        console.log('server 39 | error', err);
        throw new Error('Issue with Login', err.message);
      }

      const refreshToken = tokens.refresh_token; // <- Store it in your DB

			// Change the slashes to something that don't breakes the URL
      res.redirect(
        `YOUR_WEB_APP_ROUTE${refreshToken.replace(/\//g,'%2F' 
        )}`
      );
    });
  } catch (error) {
    res.redirect('YOUR_WEB_APP_ROUTE');
  }
};
```

Next, from your web application, you can send an event object with the necessary data to create an event in the user's Google Calendar. You can reference the required structure [here](https://developers.google.com/calendar/api/v3/reference/events/insert).

To ensure that the user is not repeatedly prompted for authorization, be sure to use the previously created and stored refresh token. This will allow your application to access the user's Google Calendar data without requiring additional permission grants.

```jsx
export const createEvent = async (req, res, next) => {
  try {
    const { refreshToken, event } = req.body;

    oAuth2Client.setCredentials({
      refresh_token: refreshToken,
    });

    const eventResponse = await calendar.events.insert({
      auth: oAuth2Client,
      calendarId: 'primary',
      conferenceDataVersion: 1,
      requestBody: event,
    });

    res.send({ data: eventResponse.data });
  } catch (error) {
    res.send({ error: error.message });
  }
};
```

Awesome! With the event ID and refresh token, you can now edit or delete events within the user's Google Calendar. This provides greater flexibility and control over the events created by your application.

```jsx
export const updateEvent = async (req, res, next) => {
  const { refreshToken, eventID, event } = req.body;

  oAuth2Client.setCredentials({
    refresh_token: refreshToken,
  });

  try {
    const updateResponse = await calendar.events.patch({
      auth: oAuth2Client,
      calendarId: 'primary',
      eventId: eventID,
      resource: event,
    });

    res.send({ data: updateResponse.data });
  } catch (error) {
    console.log(error);
    res.send({ error: error.message });
  }
};

export const deleteEvent = async (req, res, next) => {
  const { refreshToken, eventID } = req.body;

  oAuth2Client.setCredentials({
    refresh_token: refreshToken,
  });

  try {
    const deleteResponse = await calendar.events.delete({
      auth: oAuth2Client,
      calendarId: 'primary',
      eventId: eventID,
    });

    res.send({ data: deleteResponse.data });
  } catch (error) {
    console.log(error);
    res.send({ error: error.message });
  }
};
```

Don’t forget to add the routes!

`src/routes/googleCalendar.route.js`

```jsx
import { Router } from 'express';
import {
  createEvent,
  generateAuthorizationUrl,
  redirect,
  updateEvent,
  deleteEvent,
} from '../controllers/googleCalendar.controller.js';

const googleOAuthRouter = Router();

googleOAuthRouter.get('/', redirect);
googleOAuthRouter.get('/generate-authorization-url', generateAuthorizationUrl);
googleOAuthRouter.post('/create-event', createEvent);
googleOAuthRouter.put('/update-event', updateEvent);
googleOAuthRouter.delete('/delete-event', deleteEvent);

export default googleOAuthRouter;
```