Cotter + Cloudflare Workers + Google Calendar API: Build a Calendly Clone using Google OAuth

Build a serverless Calendar Booking App using Cotter, Cloudflare Workers, and Google Calendar API.


PUTRI KARUNIA

3 NOV 2020 • 12 MIN READ





During this pandemic, scheduling a meeting has become more important than ever, be it for a conference call, a consultation, or just a friendly chat.



In this guide, we'll explore how to use Google Calendar's API to make your own calendar booking app (yes, it's a platform, so other people can log in and share their own calendar link!).







We'll use:

  • Cotter for logging in users and connecting to their Google Account.

  • Cloudflare Workers as a backend that will call Google Calendar API.

  • Google Calendar API for booking time slots in your users' calendar.

  • We'll also use React in this tutorial.



Why Cloudflare Workers?

Cloudflare workers allows you to deploy JavaScript code on Cloudflare's Edge network. This means you can write serverless code in Cloudflare workers and have it deployed across the globe. Your users will connect to the nearest Cloudflare network.



What about a database?

Cloudflare workers offers a Key-Value store that can keep consistent data across their locations. Note that currently, it's "eventually consistent" which means it might take some time (max 60s) for an update to be available globally.



Why do we need a backend?

The biggest reason why we need a backend in this tutorial is that we need a secure environment to store our API Secret Key which is used to get Google's Access Tokens from Cotter.

We don't need a database or a key-value store for our use case, so Cloudflare Workers is perfect for deploying a simple backend code that authenticates your users and calls Google's API



TLDR

To try it out right now, you can use our CodeSandbox and follow the steps below to set up the Cloudflare Backend:



1. Create a project at Cotter, then add Sign in with Google to your Login Form



2. Use this CodeSandbox to get the calendar-booking React App. Update src/constants.js with your Cotter API KEY ID. You can also clone the project from the Github Repo.



3. Setup Cloudflare Worker, make 3 workers with subdomains:

  • checkconnection.YOURSUBDOMAIN.workers.dev

  • createevent.YOURSUBDOMAIN.workers.dev

  • disconnect.YOURSUBDOMAIN.workers.dev



4. Enable Google Calendar API in your Google project

Enable Google Calendar API to the project that you used to create Google OAuth 2.0 Credentials.



How it works

Here's the outline of the things that we needed to do to build this platform:

  • Users will be using "Sign in with Google" to get an access token from Google. This access token is accessible via Cotter's API.

  • 

  • Make API endpoints at your Cloudflare Worker which would 1) call Cotter's API to get the user's Google Access Token, and 2) call Google's API using that Google access token.

Logging In

Getting Google's Access Token via Google Sign In

To access Google's Calendar API, you'll need a Google Access Token for the user to modify their calendar. This means the user needs to connect their Google Account. Using Cotter as your authentication service, this can be done in 2 ways:

  • If the user logged-in using Google Sign In, you can automatically access their Google Access Token by calling Cotter's API.
  • If the user logged-in with their email, you can provide a button on the dashboard and ask them to connect their Google Account. After that, you can access their Google Access Token by calling Cotter's API.
When your user logged-in using Cotter, you'll get a Cotter Access Token. We'll use this access token to protect our API Endpoints that we're going to make in Cloudflare Workers.

1) Adding Sign in with Google to your Login Form

  1. Follow these instructions to set up Google OAuth 2.0 Credentials and connect it to Cotter. Make sure you added https://www.googleapis.com/auth/calendar for the scope.
  2. Enable Google for your form: Go to Branding > Magic Link Form > click the checkmark to enable Google Sign In
  3. Add Cotter's Login Form to your website

Using Cotter's React SDK, you can easily use React context provider to keep track of the authentication state (i.e. is the user logged-in, etc).

yarn add cotter-react
import React from "react";import { Router } from "@reach/router";import LoginPage from "../login";import { CotterProvider, LoginForm } from "cotter-react"; // 👈  Import Cotter Provider

function App() {
  return (
    // 👇 1) Wrap CotterProvider around your ROOT COMPONENT
    // Copy paste your Cotter API Key ID below.
    <CotterProvider apiKeyID="<YOUR API KEY ID>">
      <Router>
        <LoginPage path="/" />
      </Router>
    </CotterProvider>
  );}

function LoginPage() {
  return (
      <div className="LoginPage__form-container">
      {/* 👇  2) Show the login form */}
      <LoginForm
          onSuccess={(response) => console.log(response)}
          onError={(err) => console.log(err)}
      />
      </div>
  );}

export default App;



2) Checking if the user is logged-in in your dashboard

Using CotterContext you can check if the user is logged-in and get their Cotter Access Token.

import { CotterContext, withAuthenticationRequired } from "cotter-react"; // 👈  Import the user contextimport React, { useContext, useEffect, useState } from "react";import "./styles.css";

function DashboardPage() {
  const { user, getAccessToken, isLoggedIn } = useContext(CotterContext); // Get the logged-in user information
  const [accessToken, setaccessToken] = useState(null);

  useEffect(() => {
    if (isLoggedIn) readAccessToken();
  }, [isLoggedIn]);
    
  // Get the Cotter access token to be used for our API call
  // (for now, we'll just display it in the dashboard)
  const readAccessToken = async () => {
    const token = await getAccessToken();
    setaccessToken(token?.token);
  };
  return (
      <div className="container">
      	User ID: {user?.ID} <br />
      	User email: {user?.identifier} <br />
        Cotter Access Token: {accessToken}
      </div>
  );}

// Protect this page using the `withAuthenticationRequired` HOC// If user is not logged-in, they'll be redirected to the `loginPagePath`export default withAuthenticationRequired(DashboardPage, {
  loginPagePath: "/",});
  • Wrap your component around withAuthenticationRequired to automatically redirect your user to login if the user is not logged-in
  • Use isLoggedIn to check if the user is logged-in, and user to get the logged-in user information.
  • Use getAccessToken to get the Cotter Access Token that we'll use to call our API endpoints.

3) Checking if the user has connected their Google Account

We'll make an endpoint for this on our Cloudflare Worker which will call Cotter's API to see if the user has connected their Google Account.

4) Show a button to connect Google Account from the Dashboard

If the user has not connected their Google Account (they didn't log in using Google), show a button to connect Google Account. Call this function with your button to connect their Google Account:

import { CotterContext } from "cotter-react"; // 👈  Import the user context

function DashboardPage() {
  ...
  const { getCotter } = useContext(CotterContext);

  // If the user hasn't connected their Google Account,
  // show a button to call this function to connect
  const connectToGoogle = async () => {
    var cotter = getCotter();
    cotter.connectSocialLogin("GOOGLE", accessToken);
  };
    
  return (
    <div className="DashboardPage__container">
     ...
      <button onClick={connectToGoogle}>Connect Google Account</button>
    </div>
  );}

Cloudflare Worker Endpoints

Making API endpoints at Cloudflare Workers to call Google's API

Once you got the login flow working, you can start making the endpoints at Cloudflare Workers that would call Google's API. Specifically, you'll need these endpoints:

  1. An API to check if the user has connected their Google Account
  2. An API to modify their Google Calendar

Create a Cloudflare Account and Setup Workers

  1. Go to Cloudflare and sign up with your email and password. You don't need to add a domain. When asked to add a domain, if you don't have a domain, you can skip this by clicking on the Cloudflare logo.
  2. Click Workers on the right side of your dashboard, and add a subdomain that you want for your backend API endpoint. Choose the Free Plan and verify your email.

Create a Test Worker to see how this works

  1. Press Create a Worker. You can see that you have a handler script on the left side, and a playground to test your API endpoint. Click Send and you should see hello world as the response.
  2. That's it, you've just created an endpoint! To save this endpoint, press Save and Deploy.

Creating Endpoints

Notice that you can only change the sub-subdomain, not the path.

To be able to handle different paths, you'll need to use their CLI and use a router. That's too advanced for this tutorial, so we'll just have our endpoints on different subdomains.

Endpoints

Endpoint #1: checkconnection

Check if the user has Google Connected.

Create a worker, and change the name to checkconnection . We'll define our endpoint as the following:

$ curl --request GET \
    --header 'Authorization: Bearer <COTTER_ACCESS_TOKEN>' \
    --url 'https://checkconnection.YOUR_SUBDOMAIN.workers.dev?userid=<COTTER_USER_ID>'
// If Google account is connected:
{"connected": true}

// If not:
{"connected": false}

Here are the things that we'll need to do:

Code & Explanations

Copy the code from our Github repo.





Endpoint #2: createevent

Create a Google Calendar Event

Before we start, you'll need to Enable Google Calendar API to the project that you used to create Google OAuth 2.0 Credentials.

Create another worker, and change the name to createevent . We'll define our endpoint as the following:

$ curl --request POST \
  --header "Content-Type: application/json" \
  --data '{"start_time":"2020-11-02T10:00:00-08:00","end_time":"2020-11-02T10:15:00-08:00","attendee_email":"attendee@gmail.com","meeting_title":"John Doe <> Jane Doe"}' \
  --url 'https://createevent.YOUR_SUBDOMAIN.workers.dev?userid=<COTTER_USER_ID>'
  • start_time & end_time should be in formatted according to RFC3339
  • attendee_email is the email of the attendee (just 1 email)
  • meeting_title is the title of the meeting
{
    "success": true,
    "event": { ... Google's Event Obj }}
{
    "success": false,
    "error": "Error message"}

This URL doesn't require an authorization header because we want anyone to be able to book an appointment without having to log in to your platform first.

Here are the things that we'll need to do:

Code & Explanations

Copy the code from our Github repo.

Endpoint #3: disconnect

Disconnect the user's Google Account

Create another worker, and change the name to disconnect . We'll define our endpoint as the following:

$ curl --request DELETE \
    --header 'Authorization: Bearer <COTTER_ACCESS_TOKEN>' \
    --url 'https://disconnect.YOUR_SUBDOMAIN.workers.dev?userid=<COTTER_USER_ID>'
// If disconnected successfully
{"success": true}

Here are the things that we'll need to do:

Code

Copy the code from our Github repo.

Add the Cotter Api Keys as environment variables

  • Again, we need to add the API_KEY_ID and API_SECRET_KEY environment variables from Cotter API Keys.

That's it!

You should now have your backend API set up and Google Sign In set up, all you need to do is connect them in your frontend. If you need some references on how to do that, check out our Github repo:

Try the live demo, it's fully functional


Questions & Feedback

Come and talk to the founders of Cotter and other developers who are using Cotter on Cotter's Slack Channel.

Ready to use Cotter?

If you enjoyed this tutorial and want to integrate Cotter into your website or app, you can create a free account and check out our documentation.

If you need help, ping us on our Slack channel or email us at team@cotter.app.

Made in Typedream