LoginSignup
5
7

More than 3 years have passed since last update.

Go + GmailAPIでサービスアカウントからメールを送信する

Last updated at Posted at 2019-12-10

はじめに

GOでGmailAPIからメールを送ろうとした時、
サービスアカウントを利用しようとすると一気に情報がなくなって苦戦したのでメモ

やりたいこと

  • GSuiteのドメイン内のユーザが送信するメールの自動化
  • ユーザが認証をせず、ユーザのアドレスから送信したように見せかけたい

いいたいこと

  • サービスアカウントにドメインの権限を委任しよう
  • サービスアカウントクライアントにGSuiteの権限を委任しよう
  • JWTを利用した認証をしよう

今回のサンプルリポジトリ
https://github.com/siro33950/go-send-gmail-sample

サービスアカウントとは

ユーザが操作をしなくても様々な権限を付与できるアカウント
https://cloud.google.com/iam/docs/service-accounts?hl=ja

サービス アカウントは、個々のエンドユーザーではなく、アプリケーションや仮想マシン(VM)に属している特別な Google アカウントです。
アプリケーションはサービス アカウントを使用して、ユーザーの関与を必要とせずに Google のサービス API を呼び出すことができます。

サービスアカウントを利用することで以下のメリットがある

  1. ユーザがOAuth認証をしなくても権限付与できる
  2. Token切れ等を心配しなくていい

サービスアカウント作成

  1. GCPで IAMと管理 -> サービスアカウント-> サービスアカウントを作成 と移動する
    68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3131383134372f64666666623636322d616535652d623632642d303439382d6433336432303332633334362e706e67.png

  2. サービスアカウント名だけ入力し、他オプションはデフォルトで作成
    68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3131383134372f31323563303266382d326137312d643332312d356130322d6133393231303939303837652e706e67.png

  3. 今作ったサービスアカウントの詳細画面に移動
    68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3131383134372f63313037646566302d376536392d303864322d653430612d6430346531363665383463352e706e67.png

  4. 「G Suite ドメイン全体の委任を有効にする」にチェック
    スクリーンショット 2019-12-10 19.05.21.png

  5. 「キーを作成」からJOSNキーを作成
    68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3131383134372f65316432353437622d366236342d393533632d653035302d3533653166383561666533662e706e67.png

  6. キーが作成されたのを確認したら設定を保存
    スクリーンショット 2019-12-10 19.08.59.png

サービスアカウントに権限を委任

  1. GSuiteで セキュリティ -> 設定 -> 詳細設定 -> APIクライアントアクセスを管理する に移動
    スクリーンショット 2019-12-10 19.31.59.png

  2. クライアントIDにサービスアカウントのメールアドレス、スコープに利用したいAPIのスコープを記載して承認

    スコープはここに一覧がある
    68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3131383134372f30633539323362622d396531362d623664362d616665652d6265653438303430343066372e706e67.png

キーを利用して認証

先ほど作成したJSONを利用して認証する
GmailAPIのパッケージでは4種類ほど認証方法が記載されているが、
今回やりたいことはどの方法も上手くいかないので、JWTを利用した認証をする

import (
    "context"
    "golang.org/x/oauth2/google"
    "google.golang.org/api/gmail/v1"
    "google.golang.org/api/option"
    "io/ioutil"
    "log"
)

func createService() (service *gmail.Service, err error) {
    // サービスアカウント作成時にダウンロードしたJSONを読み込む
    json, err := ioutil.ReadFile("./credential.json")
    if err != nil {
        log.Printf("[ERROR] Failed to process read file: %s", err)
        return nil, err
    }

    // スコープはGSuiteで指定した物をそのまま記述する
    config, err := google.JWTConfigFromJSON(json, gmail.MailGoogleComScope)
    if err != nil {
        log.Printf("[ERROR] Failed to process get jwt config: %s", err)
        return nil, err
    }
    // 送信元のアドレスを指定
    // 別のアドレスを指定すると実行時エラーになる
    config.Subject = "hoge@gmal.com"

    ctx := context.Background()
    tokenSource := config.TokenSource(ctx)
    return gmail.NewService(ctx, option.WithTokenSource(tokenSource))
}

メールを送信

import (
    "encoding/base64"
    "google.golang.org/api/gmail/v1"
    "log"
    "strings"
)

func sendMail() {
    // サービス作成
    service, err := createService()
    if err != nil {
        log.Printf("[ERROR] Failed to process create service: %s", err)
        return
    }

  // 本文を作成
  // FROMはSubjectに指定したアドレスか、そのアドレスが利用できるaliasを指定する
    temp := []byte("From: hoge@gmal.com\r\n" +
        "To: fuga@gmail.com\r\n" +
        "Subject: testSubject\r\n" +
        "\r\ntestBody")
    var message gmail.Message
    message.Raw = base64.StdEncoding.EncodeToString(temp)
    message.Raw = strings.Replace(message.Raw, "/", "_", -1)
    message.Raw = strings.Replace(message.Raw, "+", "-", -1)
    message.Raw = strings.Replace(message.Raw, "=", "", -1)

  // 送信
    _, err = service.Users.Messages.Send("me", &message).Do()
    if err != nil {
        log.Printf("[ERROR] Failed to process send message: %s", err)
        return
    }
}

参考

https://qiita.com/tnagao3000/items/3d210582bc7f1ca218cc
https://godoc.org/google.golang.org/api/gmail/v1

5
7
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
5
7