はじめに
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 を呼び出すことができます。
サービスアカウントを利用することで以下のメリットがある
- ユーザがOAuth認証をしなくても権限付与できる
- Token切れ等を心配しなくていい
サービスアカウント作成
サービスアカウントに権限を委任
-
クライアントIDにサービスアカウントのメールアドレス、スコープに利用したいAPIのスコープを記載して承認
スコープはここに一覧がある
キーを利用して認証
先ほど作成した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