Google Calender API Logo

【トークン取得編】ユーザのGoogleカレンダー情報を取得できる、よく見る同意画面を実装する方法

2024/11/10

背景

趣味で開発しているアプリのカレンダー機能を充実させたいと思い、Google Calendar の情報を表示する機能を追加することにしました。
そこで、ユーザーが簡単に Google アカウントと連携できるよう、いつも見るような同意画面で Google Calendar 連携できるようにしたいと思い実装しました。

よく見るGoogle同意画面

概要

処理のイメージは以下のような感じです。

  1. ユーザーが連携ボタンをクリック
  2. Google Cloud で設定した際に取得した Client ID などを使って、認証用の URL を生成
  3. ブラウザに認証用の URL にアクセスさせる(リダイレクト)
  4. Google 専用の同意画面が表示され、ユーザが Google アカウントの選択と同意を行う
  5. 設定した callback URL に認証コード付きでアクセスされる(リダイレクト)

Google Cloud の設定

Google Calendar を取得するには、Google Cloud で Google Calendar API を有効にしたり、トークンを取得できるように設定する必要があります。

  1. Google Cloud Consoleにアクセスし、検索窓で[Google Calendar API]を検索して有効にする

【トークン取得編】ユーザのGoogleカレンダー情報を取得できる、よく見る同意画面を実装する方法01

  1. 検索窓で[API とサービス]を検索し、[OAuth 同意画面] メニューを選択し、[外部]を選択して作成する

【トークン取得編】ユーザのGoogleカレンダー情報を取得できる、よく見る同意画面を実装する方法02

  1. アプリの情報を入力する

【トークン取得編】ユーザのGoogleカレンダー情報を取得できる、よく見る同意画面を実装する方法03

  1. スコープ情報を入力する

【トークン取得編】ユーザのGoogleカレンダー情報を取得できる、よく見る同意画面を実装する方法04

  1. テストユーザのメールアドレスを入力する

【トークン取得編】ユーザのGoogleカレンダー情報を取得できる、よく見る同意画面を実装する方法05

  1. [同意情報]の入力後、[認証情報]メニューを選択、[認証情報を作成]を選択し、[OAuth クライアント ID]を選択する

【トークン取得編】ユーザのGoogleカレンダー情報を取得できる、よく見る同意画面を実装する方法06

  1. ウェブアプリケーションを選択し、名前やリダイレクト先を入力し、作成ボタンを押下する

【トークン取得編】ユーザのGoogleカレンダー情報を取得できる、よく見る同意�画面を実装する方法07

  1. Client ID と Client Secret が表示されるので、それぞれを大切にメモしておく

【トークン取得編】ユーザのGoogleカレンダー情報を取得できる、よく見る同意画面を実装する方法08

実装

Next.js の API 機能や Node.js での実装例が多くありますが、今回は Go でのバックエンドを選択しました。
以下がその実装例です。

フロントエンドの部分は Google 認証用の URL を生成する API を呼び出すように実装するだけです。(概要※1)

import Link from "next/link"
import { type FC, useEffect } from "react"

export const SettingPage: FC = () => {
  const googleOAuthUrl = "http://localhost:8000/v2/auth/google-oauth/request"

  return (
    <div>
      <Link href={googleOAuthUrl}>Google連携</Link>
    </div>
  )
}

フロント側で Google 連携ボタンがクリックされた時に呼ばれる API は以下のようになります。(概要※2,3)

// 「http://localhost:8000/v2/auth/google-oauth/request」が叩かれた際に呼ばれる関数
func V2GoogleOAuthRequestGetController(ctx *gin.Context) {
  config := &oauth2.Config{
    // [Google Cloud の設定 | 8. Client ID と Client Secret が表示されるので、それぞれを大切にメモしておく] で取得した値を入れる
    ClientID:     "12345-hogefuga.apps.googleusercontent.com",
    ClientSecret: "HOGE-FUGA",
    Endpoint:     google.Endpoint,
    // アプリとして欲しい権限を指定する
    Scopes: []string{
      "https://www.googleapis.com/auth/calendar.calendars.readonly",
      "https://www.googleapis.com/auth/calendar.calendarlist.readonly",
    },
    RedirectURL: "http://localhost:8000/v2/auth/google-oauth/callback",
  }

  // AuthCodeURLの第一引数に入れた値がcallback URLに state として渡されるので、ユーザーを特定できるIDなどを入れる
  userID, _ := ctx.Value("userId").(string)
  url := config.AuthCodeURL(userID, oauth2.AccessTypeOffline)
  ctx.Redirect(http.StatusTemporaryRedirect, url)
}

ユーザが Google の同意画面で同意を行った後にリダイレクトされた時に呼ばれる API は以下のようになります。(概要※5)

// 「http://localhost:8000/v2/auth/google-oauth/callback」が叩かれた際に呼ばれる関数
func V2GoogleOAuthCallbackGetController(ctx *gin.Context) {
  // ~/callback?state=userId&code=authCode という形でリダイレクトされるので認証コードを取得
  code := ctx.Query("code")

  // ~/callback?state=userId&code=authCode 認証URLを作成した際に設定した値を取得
  userID, _ := uuid.Parse(c.Query("state"))

  config := &oauth2.Config{
    ClientID:     "12345-hogefuga.apps.googleusercontent.com",
    ClientSecret: "HOGE-FUGA",
    Endpoint:     google.Endpoint,
    Scopes: []string{
      "https://www.googleapis.com/auth/calendar.calendars.readonly",
      "https://www.googleapis.com/auth/calendar.calendarlist.readonly",
    },
    RedirectURL: "http://localhost:8000/v2/auth/google-oauth/callback",
  }

  // GoogleからTokenを貰う
  oAuth2Token, _ := config.Exchange(ctx, code)

  // DBなどにTokenを保存する
  save(oAuth2Token, userID)

  // 連携完了後に表示したい画面にリダイレクトする
  c.Redirect(http.StatusTemporaryRedirect, "http://localhost:3000/setting")
}

最後に

ユーザの Google Calendar 情報を取得できるトークンの取得はこれで完了です。
他にもトークンをリフレッシュしたり、トークンを使って Google Calendar の情報を取得する必要があります。
そこらへんの実装はまた別の記事で書きたいと思います。

宣伝: @TTrpbm

参考にさせていただいた記事

Google Cloud Platform(GCP)の OAuth 認証の設定
Google 認証と OAuth2.0 で、Google サービスと連携する例のアレを作る
Golang で Google OAuth でのアカウント連携をする

Twitterフォロー待ってます!