Come integrare l’API di Google Calendar nella tua app

Pubblicato il

In questa guida spieghiamo passo dopo passo come integrare la Google Calendar API nella tua app. Copriamo la configurazione del progetto Google Cloud, gli scope necessari, i problemi più comuni e un esempio reale di flusso di autorizzazione.

Prerequisiti

Questa guida presuppone che tu abbia un indirizzo email, un dominio da usare durante la configurazione del progetto Google Cloud, un po’ di esperienza di programmazione e un’idea abbastanza chiara di ciò che vuoi costruire.

La guida è utile anche se non hai mai lavorato con la Google Calendar API e vuoi capire ogni passaggio necessario per aggiungerla alla tua app.

Come usare e integrare la Google Calendar API nella tua app

1. Registrati alla Google Developer Console

Se non hai un account Google Developer Console, creane uno su https://console.cloud.google.com/.

2. Crea o scegli un progetto Google Cloud esistente

Google Cloud permette a sviluppatori e organizzazioni di avere più progetti. Assicurati di essere nel progetto giusto prima di eseguire i passaggi qui sotto.

Fai clic sul menu a discesa dei progetti in alto a sinistra. Il nome di solito corrisponde al nome del tuo progetto.

Google Cloud Home

Seleziona un progetto esistente oppure crea un nuovo progetto cliccando "New Project" in alto a destra del modale.

Google Cloud - Select Project

3. Abilita i servizi Google Calendar API

Una volta che hai un account e sei nel progetto giusto, segui questi passaggi per abilitare i servizi Google Calendar API:

  1. Vai alla Google Cloud Console
  2. Fai clic su “APIs & Services” Google Cloud - Click APIs and Services
  3. Fai clic su “Enable APIs and services” Google Cloud - Enable APIs and services
  4. Cerca “Google Calendar API” Search for Google Calendar API service
  5. Fai clic su "Enable" per abilitare il servizio. Enable Google Calendar API service

4. Configura la schermata di consenso OAuth

Dopo aver abilitato i servizi Google Calendar API, il passo successivo è configurare la schermata di consenso OAuth. È la schermata che gli utenti finali vedono quando collegano il loro calendario alla tua app. Di solito mostra il logo, il nome dell’app, i permessi che richiedi e altro.

  1. Clicca sulla scheda "OAuth Consent Screen". Click OAuth Consent Screen
  2. Clicca "Get Started". Click Get Started
  3. Compila la sezione “App Information”. Inserisci il nome dell’app e l’email di supporto. Fill in the “App Information” section
  4. Scegli il pubblico. Può essere interno o esterno. Scegli interno se l’app non sarà pubblica e solo gli utenti della tua organizzazione possono collegarsi; esterno se possono accedere anche account pubblici. Choose the audience
  5. Inserisci le informazioni di contatto. Google richiede un’email per notificare eventuali modifiche al progetto. How to Integrate Google Calendar API Into Your App
  6. Spunta la casella "Agree to the Google API Services: User Data Policy". Check the Agree to the Google API Services
  7. Clicca “Create”. Click “Create”

5. Crea il tuo client OAuth

Una volta configurata la schermata di consenso OAuth, puoi creare il client OAuth per il tuo progetto. Clicca la scheda “Clients” e poi “Create Client”.

In alternativa, clicca "Create OAuth Client" nella pagina di panoramica.

Create OAuth Client

Puoi creare un client per ogni piattaforma su cui gira la tua app. Ad esempio, se sviluppi una web app e un’app iOS, ti serve un ID client OAuth separato per ciascuna.

Google Cloud - Client Options

In questo esempio, creiamo un client “Web Application” chiamato “Web Client”.

Google Cloud - Web Client

Nello stesso flusso impostiamo anche gli authorized JavaScript origins e gli authorized redirect URI.

Nel campo Authorized JavaScript origins inserisci il dominio/URL che ospita la tua web app, ad esempio: myapp.domain.com

Google Cloud - Authorized Domains

Nel campo Authorized redirect URIs inserisci tutti gli URL a cui reindirizzerai gli utenti dopo l’autenticazione con Google. Google aggiunge il codice di autorizzazione a questo URL, e l’URL deve includere il protocollo.

Google Cloud - Setup Redirect URL

Dopo aver compilato i campi, clicca “Create”. Google apre quindi un modale che mostra Client ID e Client Secret. Copia entrambi i valori e conservali in un luogo sicuro (di solito nel file .env, perché li usiamo nel flusso di autenticazione qui sotto). Puoi anche scaricare il file JSON e salvarlo in un gestore di segreti come 1Password.

OAuth Client Created - Modal

6. Aggiungi alcuni utenti di test

Durante lo sviluppo in locale non puoi collegare alcun account Google Calendar finché non aggiungi alcuni utenti di test. Il motivo è che l’app è esterna e non è ancora stata approvata da Google.

Per aggiungere utenti di test:

  1. Clicca la scheda “Audience”. Google Cloud - Audience Tab
  2. Scorri fino alla sezione “Test Users”. Test Users Section
  3. Clicca “Add Users” e inserisci l’email dell’utente. Add Test users by email

7. Aggiungi gli scope di calendario che intendi usare

A seconda del caso d’uso che vuoi risolvere con l’integrazione, potresti dover richiedere scope diversi quando gli utenti autorizzano il loro account Google Calendar nella tua app.

Pensa agli scope come ai permessi che chiedi agli utenti. Permettono alla tua app di accedere ai dati privati del loro account Google, ad esempio per elencare i calendari o leggere gli eventi.

Google divide gli scope in sensibili e non sensibili. Se aggiungi scope sensibili, devi inviare l’app per la verifica. Lo stesso vale se l’app è già verificata e aggiungi altri scope sensibili.

Per gestire gli scope:

  1. Clicca la scheda "Data Access". Data Access Tab
  2. Clicca "Add or remove scopes". Add Or Remove Scopes Button
  3. Cerca lo scope per nome o valore e aggiungilo. Select Scopes Section

8. Familiarizza con la Google Calendar API

Ora che l’app client di Google è configurata e tutte le informazioni necessarie perché gli utenti colleghino i loro calendari sono al loro posto, è il momento di familiarizzare con la Google Calendar API stessa.

Consigliamo di leggere la pagina panoramica della Google Calendar API e di guardare gli endpoint più importanti, come quelli di Events e Calendars.

9. Usa un servizio Unified Calendar API per integrare più provider con una sola API

Se Google Calendar è l’unico calendario che vuoi integrare, puoi saltare questo passaggio. Altrimenti ti consigliamo di usare una Unified Calendar API, che ti offre un’unica API per tutti i provider di calendari.

Con una Unified Calendar API costruisci e mantieni una sola integrazione per tutti i provider. Se in seguito decidi di supportare Outlook o iCloud, puoi aggiungerli senza scrivere una nuova integrazione.

Eviti anche di mantenere più integrazioni, di gestire breaking change e di passare tempo a imparare i dettagli dell’API di ogni provider.

Esempio di flusso di autorizzazione Google Calendar

Il diagramma qui sotto mostra un semplice flusso OAuth che permette agli utenti di collegare il loro Google Calendar alla tua app.

Google Calendar OAuth flow

Google offre librerie client per Node.js, Python e altri linguaggi. Per semplicità, usiamo solo chiamate HTTP semplici e TypeScript per mostrare il flusso di autorizzazione.

Lato Client (UI)

La prima parte è il lato client (UI), dove mostriamo un pulsante “Connect Google Calendar”.

const googleOauthUrl = getGoogleOAuthUrl()
<button href="googleOauthUrl"   rel="noopener noreferrer"> Connect Google Calendar </button>

Consigliamo di usare una funzione di utilità per costruire l’URL OAuth di Google. Mantiene il codice leggibile e rende facile passare parametri come lo stato del client o il consenso forzato.


const SCOPES = [
  "openid",
  "email",
  "<https://www.googleapis.com/auth/calendar.calendarlist>",
  "<https://www.googleapis.com/auth/calendar.events>",
  "<https://www.googleapis.com/auth/calendar.readonly>",
  // add more scopes as needed
];

export interface ClientState {
  session: Session;
  returnUrl?: string;
}

export function stateToB64(session: ClientState): string {
  return encode(JSON.stringify(session));
}

export function getGoogleOAuthUrl(
  state: ClientState,
) {
  const params = new URLSearchParams({
    client_id: process.env.GOOGLE_CLIENT_ID || "",
    redirect_uri: `${getHostName()}/api/connect/google`, // change the redirect URL as needed
    response_type: "code",
    scope: SCOPES.join(" "),
    prompt: "consent",
    access_type: "offline",
    state: stateToB64(state),
  });
  return `https://accounts.google.com/o/oauth2/v2/auth?${params}`;
}

Il parametro prompt può avere uno di tre valori: none, consent o select_account.

Il valore consent è utile quando un utente ha già autorizzato i suoi calendari, ma vuoi comunque che Google mostri di nuovo la schermata di autorizzazione. Succede, ad esempio, quando aggiungi nuovi scope alla tua app e vuoi che gli utenti li approvino.

Un altro caso per consent è quando l’utente non ha concesso tutti gli scope richiesti dalla tua app, quindi vuoi chiederli di nuovo.

Il valore select_account chiede all’utente di scegliere un account.

Il valore none non mostra alcuna schermata di autenticazione o consenso.

Lato API (Backend)

Creiamo poi l’handler API. Riceve il codice e gli scope da Google e scambia il codice con i token.

In questo esempio usiamo zod per la validazione.

import { z } from "zod";
const successSchema = z.object({
  code: z.string(),
  scope: z.string(),
  state: z.string(),
});

const errorSchema = z.object({
  error: z.string(),
});
type ErrorParams = z.infer<typeof errorSchema>;

const querySchema = z.union([successSchema, errorSchema]);

// Handler
const googleHanlder: NextApiHandler = async (req, res) => {
  try {
    const result = querySchema.parse(req.query);
    if (isError(result)) {
      const q = new URLSearchParams({
        error: "ACCESS_DENIED",
      });
      return res.redirect(`/?${q}`);
    }

    const { session, returnUr } = stateFromB64(
      result.state
    );

    if (!hasRequiredScopes(result.scope)) {
      const q = new URLSearchParams({
        error: "MISSING_REQUIRED_PERMISSIONS",
      });
      return res.redirect(`/?${q}`);
    }

    const { access_token, refresh_token, id_token, expires_in } =
      await exchangeCodeForTokens(result.code);
    const { email } = decodeIdToken(id_token);

		// Update or insert the calendar connection, depending on your use case
    const connection = await upsertConnection(
      {
        email,
        accessToken: access_token,
        refreshToken: refresh_token,
        expiresInSeconds: expires_in,
        status: ConnectionStatus.ACTIVE,
        provider: CalendarProvider.GOOGLE,
        scopes: result.scope,
        reminderCount: 0,
        lastRemindedAt: null,
      },
      session.user
    );

    const q = new URLSearchParams({
      cid: connection.id,
    });
    if (returnUrl) q.append("returnUrl", returnUrl);

		// The API redirects back to the client side, returning the connection, or errors if any 
    res.redirect returnUrl ? returnUrl : `/calendars/google?${q}`
    );
  } catch (e: any) {
    let error = JSON.stringify(e);

      const querystr =
        typeof req.query === "string" ? req.query : JSON.stringify(req.query);
      console.error("Error in googleHandler", querystr);
      console.error("Failed to connect Google account", e);

    const q = new URLSearchParams({
      error,
    });
    return res.redirect(`/?${q}`);
  }
};

Come sul lato client, consigliamo piccole funzioni di utilità per scambiare il codice con i token, decodificare l’ID token, leggere lo stato da base64 e così via.

export async function exchangeCodeForTokens(code: string) {
  const data = new FormData();
  data.append("code", code);
  data.append("client_id", process.env.GOOGLE_CLIENT_ID || "");
  data.append("client_secret", process.env.GOOGLE_CLIENT_SECRET || "");
  data.append("redirect_uri", `${getHostName()}/api/connect/google`); // your URL
  data.append("grant_type", "authorization_code");

  try {
    const result = await fetch("<https://oauth2.googleapis.com/token>", {
      method: "POST",
      body: data,
    });

    const json = await result.json();
    if (json.error) throw json;

    const parsed = responseSchema.parse(json);
    return parsed;
  } catch (e) {
    console.error("Exchange failed");
    throw e;
  }
}

export function decodeIdToken(idToken: string) {
  const data = jwt.decode(idToken);
  if (typeof data === "string" || !data?.email) {
    throw new Error(`Could not parse id_token: ${idToken}`);
  }

  return data;
}

function isError(query: Record<string, any>): query is ErrorParams {
  return Boolean(query.error);
}

export function stateFromB64(encoded: string): ClientState {
  const str = decode(encoded);

  return JSON.parse(str) as ClientState;
}

Possibili problemi con l’integrazione della Google Calendar API

  • La verifica può richiedere diverse settimane, quindi pianifica in anticipo e considera questo ritardo nella tua tabella di marcia. Il processo di approvazione richiede tempo ed è comune ricevere un rifiuto al primo invio. Inserisci questo ritardo nella tua pianificazione e informa tutte le persone coinvolte.
  • I webhook scadono dopo circa 24 ore, quindi rinnovali sempre. Puoi registrare webhook per rilevare le modifiche a un calendario, ma scadono dopo circa 24 ore. Un cron job può trovare i webhook che scadono nei prossimi 20 minuti e rinnovarli.
  • Richiedi solo gli scope di cui hai bisogno. Può sembrare furbo richiedere il maggior numero possibile di scope, visto che non sai mai di cosa avrà bisogno la prossima versione della tua app. Consigliamo di richiedere solo ciò che serve davvero alla tua app. Primo, Google è molto rigorosa durante la verifica e gli scope superflui portano a rifiuti e a un continuo scambio finché non ottieni l’approvazione. Secondo, gli utenti esitano a concedere permessi che non hanno senso rispetto a ciò che fa l’app.
  • Gestisci quote e rate limiting. Google applica una quota al minuto per progetto e una quota al minuto per progetto e per utente. Se superi una delle due, Google restituisce un errore 403 usageLimits o 429 rateLimitExceeded. Usa il backoff esponenziale e tattiche simili per restare sotto questi limiti.
  • Evita di registrare dati personali nei log. Loggare va bene, ma rimuovi prima i dati personali degli eventi, perché gli eventi contengono di solito descrizioni, email dei partecipanti e altri dettagli privati.

Integra più provider di calendario con la Apiroc Unified Calendar API

Il nostro team lavora da anni con Google Calendar, Outlook e iCloud Calendar e ha costruito integrazioni di calendario che gestiscono miliardi di chiamate API. Sappiamo cosa serve per integrare più provider, imparare i dettagli di ogni API e mantenere ogni implementazione affidabile nel tempo.

Per questo abbiamo creato Apiroc, una Unified Calendar API che supporta Google Calendar, Microsoft Outlook e iCloud Calendar fin da subito. Puoi integrare tutti i principali provider con un’unica API robusta e facile da usare.

Puoi iniziare gratuitamente, testare l’API con un massimo di 10 End User Account, scoprirne le funzionalità e fare l’upgrade in seguito se ti serve di più. Non è richiesta alcuna carta di credito.

Domande frequenti

Perché devo aggiungere utenti di test al mio client OAuth di Google?

Un’app esterna non verificata può essere usata solo da account di test autorizzati, durante lo sviluppo e finché Google non la approva.

Per quanto tempo restano attivi i webhook di Google Calendar?

Le sottoscrizioni alle notifiche push scadono dopo circa 24 ore. Il backend deve quindi rinnovarle prima della scadenza.

Esiste un modo più semplice per aggiungere in seguito i calendari Outlook o iCloud?

Sì. Una Unified Calendar API come Apiroc ti permette di integrare Google, Outlook e iCloud tramite un’unica API. Così risparmi il tempo e i costi di sviluppo e manutenzione di integrazioni separate.

Ho bisogno di “offline_access” per i processi in background?

Sì. Quando aggiungi access_type=offline all’URL di consenso, Google restituisce un refresh token, così il tuo server può chiamare l’API anche quando l’utente non è presente.