6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

Web開発を行うにあたってログイン機能の追加は多くの人が通る道だと思います。そんなログイン機能の中で最も使われているであろうGoogleのGmailを使用したログイン機能の実装をGo言語で行う方法をまとめた記事です。

実装手順

プロジェクトの作成

まず、Google Cloud Consoleで新しいプロジェクトを作成します。以下の手順に従ってください

プロジェクト作成画面への遷移

  1. Google Cloud Consoleにアクセスします
  2. 画面上部の「プロジェクトの作成」ボタンをクリックします
    スクリーンショット 2024-12-15 20.35.51.png

プロジェクト詳細の設定

  1. プロジェクト名を入力します。わかりやすく、プロジェクトの目的を反映した名前をつけましょう
  2. 組織に所属している場合は、適切な組織を選択します
    スクリーンショット 2024-12-15 20.37.26.png

プロジェクト作成完了

プロジェクトの作成が完了すると、以下の画面に遷移します。この画面で、新しく作成したプロジェクトが選択された状態になっていることを確認してください。
スクリーンショット 2024-12-15 20.39.07.png

OAuth2.0 クライアントの作成

Gmailログイン機能の実装には、OAuth 2.0認証プロトコルを使用します。以下の手順で認証情報を設定します。

認証情報の作成

  1. 左側のメニューから「認証情報」を選択します
  2. 画面上部の「+ 認証情報を作成」をクリックし、「OAuthクライアントID」を選択します
    スクリーンショット 2024-12-15 21.18.42.png

クライアント情報の設定

  1. アプリケーションの種類で「ウェブアプリケーション」を選択します
  2. クライアント名を入力します
  3. 承認済みのJavaScript生成元にhttp://localhost:8080を入力します
  4. 承認済みのリダイレクトURIにhttp://localhost:8080/auth/google/callbackを入力します

これらが入力できるとページ下の作成ボタンをクリックして作成してください。
スクリーンショット 2024-12-15 21.19.41.png

認証情報の取得

作成が完了すると、クライアントIDクライアントシークレットが表示されます。これらは環境変数として後の実装で使用するため、安全に保管してください。
スクリーンショット 2024-12-15 21.20.16.png

コード実装

実装前の準備

mkdir demo
cd demo
mkdir main.go
mkdir templates
touch templates/index.html
touch .env

code .

main.goの実装

main.go
package main

import (
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"

	"github.com/gin-gonic/gin"
	"github.com/joho/godotenv"
	"github.com/markbates/goth"
	"github.com/markbates/goth/gothic"
	"github.com/markbates/goth/providers/google"
)

func main() {
	// Ginルーターの初期化
	r := gin.Default()

	// .envファイルの読み込み(環境変数の設定)
	err := godotenv.Load()
	if err != nil {
		log.Fatal(".envファイルの読み込みに失敗しました!")
	}

	// 環境変数からOAuth認証に必要な情報を取得
	clientID := os.Getenv("CLIENT_ID")
	clientSecret := os.Getenv("CLIENT_SECRET")
	clientCallbackURL := os.Getenv("CLIENT_CALLBACK_URL")

	// 必要な環境変数が設定されているか確認
	if clientID == "" || clientSecret == "" || clientCallbackURL == "" {
		log.Fatal("環境変数(CLIENT_ID、CLIENT_SECRET、CLIENT_CALLBACK_URL)が必要です")
	}

	// Googleプロバイダを使用したOAuth認証の設定
	// スコープを明示的に指定:ユーザー情報とメールアクセス
	googleProvider := google.New(
		clientID,
		clientSecret,
		clientCallbackURL,
		"https://www.googleapis.com/auth/userinfo.email",
		"https://www.googleapis.com/auth/userinfo.profile",
	)

	goth.UseProviders(googleProvider)

	// HTMLテンプレートの読み込み
	r.LoadHTMLGlob("templates/*")

	// ルーティングの設定
	r.GET("/", home)                                   // トップページ
	r.GET("/auth/:provider", signInWithProvider)       // 認証開始エンドポイント
	r.GET("/auth/:provider/callback", callbackHandler) // コールバックエンドポイント
	r.GET("/success", Success)                         // 認証成功後のページ

	// サーバーの起動(8080ポート)
	r.Run(":8080")
}

func home(c *gin.Context) {
	// インデックスページのテンプレート読み込み
	tmpl, err := template.ParseFiles("templates/index.html")
	if err != nil {
		// テンプレート読み込みエラー時は500内部サーバーエラーを返す
		c.AbortWithStatus(http.StatusInternalServerError)
		return
	}

	// テンプレートの実行
	err = tmpl.Execute(c.Writer, gin.H{})
	if err != nil {
		// テンプレート実行エラー時は500内部サーバーエラーを返す
		c.AbortWithStatus(http.StatusInternalServerError)
		return
	}
}

func signInWithProvider(c *gin.Context) {
	// 認証プロバイダの取得(この場合はGoogle)
	provider := c.Param("provider")
	q := c.Request.URL.Query()
	q.Add("provider", provider)
	c.Request.URL.RawQuery = q.Encode()

	// 認証プロセスの開始
	gothic.BeginAuthHandler(c.Writer, c.Request)
}

func callbackHandler(c *gin.Context) {
	// 認証プロバイダの取得
	provider := c.Param("provider")
	q := c.Request.URL.Query()
	q.Add("provider", provider)
	c.Request.URL.RawQuery = q.Encode()

	// ユーザー認証の完了
	user, err := gothic.CompleteUserAuth(c.Writer, c.Request)
	if err != nil {
		// 認証エラー時は500内部サーバーエラーを返す
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	// ユーザー情報をログに出力
	log.Printf("ログインユーザー情報:")
	log.Printf("プロバイダ: %s", user.Provider)
	log.Printf("ユーザーID: %s", user.UserID)
	log.Printf("ニックネーム: %s", user.NickName)
	log.Printf("メールアドレス: %s", user.Email)
	log.Printf("名前: %s", user.Name)
	log.Printf("画像URL: %s", user.AvatarURL)

	// 認証成功後、successページにリダイレクト
	c.Redirect(http.StatusTemporaryRedirect, "/success")
}

func Success(c *gin.Context) {
	// 認証成功後に表示するシンプルなHTMLページ
	c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(fmt.Sprintf(`
      <div style="
          background-color: #fff;
          padding: 40px;
          border-radius: 8px;
          box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
          text-align: center;
      ">
          <h1 style="
              color: #333;
              margin-bottom: 20px;
          ">ログインに成功しました!</h1>
          
          </div>
      </div>
  `)))
}

今回の実装ではgothgothicライブラリを使用して、Googleのログイン機能を簡単に実装しています。
主に3つの重要な関数で構成されています:

1. ログイン開始 (signInWithProvider)

ユーザーがGoogleログインボタンをクリックすると、Googleの認証ページにリダイレクトされます
gothic.BeginAuthHandler()が認証プロセスを開始させる関数です

2. コールバック処理 (callbackHandler)

Googleからユーザー情報を受け取る
ログイン成功後のユーザー情報を取得
ユーザーのメールアドレス、名前、プロフィール画像などにアクセス可能

ここでセッションの追加やDBへの保存の関数の呼び出しを行う

3. 成功ページ (Success)

ログイン成功後に表示されるシンプルな画面

スコープの設定

googleProvider := google.New(
    clientID,
    clientSecret,
    clientCallbackURL,
    "https://www.googleapis.com/auth/userinfo.email",
    "https://www.googleapis.com/auth/userinfo.profile",
)

userinfo.email: ユーザーのメールアドレスにアクセス
userinfo.profile: ユーザーの基本プロファイル情報にアクセス

他の一般的なGoogleスコープ例

以下に、他のスコープに関するリファレンスがありますので以下から必要なスコープを探して追加してください。
https://developers.google.com/identity/protocols/oauth2/scopes?hl=ja

スコープ選択

スコープはとても便利ですが以下の3点を心がけて使うようにしましょう

  • 最小権限の原則に従い、必要最小限のスコープを要求
  • ユーザーのプライバシーを尊重
  • アプリケーションの目的に必要な情報のみをリクエスト

ログイン前のホーム画面の作成

templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Sign in with Google</title>
</head>
<body 
     style="
         font-family: Arial, sans-serif; 
         background-color: #f0f0f0; 
         display: flex; 
         justify-content: center; 
         align-items: center; 
         height: 100vh;"
>
  <div 
     style="
        background-color: #fff; 
        padding: 40px; 
        border-radius: 8px; 
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 
        text-align: center;"
    >
        <h1 style="color: #333; margin-bottom: 20px;">Welcome</h1>
        <a href="/auth/google"  style="
        display: inline-flex; 
        align-items: center; 
        justify-content: center; 
        background-color: #4285f4; 
        color: #fff; 
        text-decoration: none; 
        padding: 12px 20px; 
        border-radius: 4px; 
        transition: background-color 0.3s ease;">
        <span style="font-size: 16px; font-weight: bold;">Sign in with Google</span>
        </a>
  </div>
</body>
</html>

環境変数の設定

.env
CLIENT_ID=clientID
CLIENT_SECRET=clientSecret
CLIENT_CALLBACK_URL=http://localhost:8080/auth/google/callback

環境変数として、以下のものを定義しています

  • CLIENT_ID: Googleから取得したクライアントID
  • CLIENT_SECRET: クライアントシークレット
  • CLIENT_CALLBACK_URL: 認証後のリダイレクトURL

プロジェクトの依存関係 初期化

go mod init demo
go mod tidy

実装確認

以下のコマンドで実装したコードを試してみましょう

go run main.go

エラーなく実行できていたら以下のURLにアクセスしてみてください

http://localhost:8080/

アクセスするとこのようにログインのためのボタンだけのページが表示されます。
スクリーンショット 2024-12-15 22.17.04.png

ログインが成功すると以下のような画面になります。
また、実行しているGoのターミナルで実際にログインしたユーザーの情報を確認できます。
スクリーンショット 2024-12-15 22.16.49.png

参考

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?