Gmail API + phpでメールを送ってみる

自分用のWebアプリケーションから、長らく`PHPMailer`を使用して特定のアカウントからGmailメールを送信していました。セキュリティが厳しくなり、以下のような対応をしていました。
* ~~Googleアカウントで安全性の低いアプリのアクセスを許可~~
* アプリパスワードを作成してSMTPパスワードの替わりに使用　[こちらを参考](https://taitan916.info/blog/archives/3954)

このたび、SMTPでなくOAuth認証にし、Gmail APIを使って送信してみることにしました。

## 全体の流れ

1. Google Cloudの設定
1. クライアントライブラリのインストール
1. PHPコード作成

## Google Cloud設定
[Google Cloud Consoleにログイン](https://console.cloud.google.com/)して以下を設定する。

### アプリ情報の設定
1. **APIとサービス > OAuth同意画面 > ブランディング**を選択
    項目|設定値
    ---|---
    |**アプリ名**  |メールを送信するアプリケーションの名前  |
    |**ユーザーサポートメール**  |このアプリについての問い合わせメールアドレス  |
    |**承認済みドメイン**  |メール送信アプリを置くサーバーのドメイン |

:::note info
後述する認証情報の**承認済みのリダイレクト URI**は、**承認済みドメイン**を含まなければならない。
:::

### Gmail APIを有効化
1. **APIとサービス > 有効なAPIとサービス**を選択
1. **+APIとサービスを有効にする**をクリック
**Gmail API**を選択し、**有効にする**クリック

### 認証情報を作成
1. **APIとサービス > 認証情報**を選択
1. **+認証情報を作成**をクリック、**OAuth クライアント ID**を選択
    項目|設定値
    ---|---
    |**アプリケーションの種類**  |"ウェブアプリケーション"  |
    |**名前**  |任意。管理上識別できれば良い。  |
    |**承認済みの JavaScript 生成元**  |<今回は不要>  |
    |**承認済みのリダイレクト URI**  |認証後にGoogleから呼ばれるURLを追加。プロトコルから正確に記述する。  |
 1. **作成**クリックでクライアントが作成されたら、**設定情報をダウンロード**する。
     一覧の右側の**ダウンロードアイコン**をクリック。 ここではclient_secret.jsonとする。

### スコープの設定 
1. **APIとサービス > OAuth同意画面 > データアクセス**を選択
1. **スコープを追加または削除**をクリック。
1. **../auth/gmail.send**を選択し、**更新**ボタンクリック
1. **保存**クリック
:::note info
スコープは必要最小限を選ぶ
:::

### 対象（オーディエンス）の設定
1. **APIとサービス > OAuth同意画面 > 対象**を選択
    ユーザーの種類|説明
    ---|---
    |**内部**  |組織内のユーザーのみ許可(**Google Workspaceユーザーでないと選択できない**)  |
    |**外部**  |Googleアカウントを持つユーザーは誰でも使用できる  |
:::note info
外部の場合は色々設定しないとならなので、とりあえずテスト状態にしてユーザーを制限する
:::
    
## クライアントライブラリの導入
1. **APIとサービス > ライブラリ**を選択
1. **Google Workspace**グループの**Gmail API**を選択
1. **このAPIを試す**を選択
    [Gmail APIリファレンス](https://developers.google.com/gmail/api/reference/rest?apix=true&hl=ja)にリダイレクトされる
1. Gmail APIリファレンス画面左のフレームから、**クライアントライブラリ > 概要**
https://developers.google.com/gmail/api/downloads?hl=ja
1. PHPタブから[PHP 用 Gmail API クライアント ライブラリ（ベータ版）](https://github.com/googleapis/google-api-php-client)
    Githubリポジトリ（**googleapis/google-api-php-client**）が開く。
1. READMEの通り、PHPライブラリをサーバーにインストール
`composer require google/apiclient`
:::note info
リポジトリの/doc/oauth-web.mdがコーディングの参考になります。
:::

## PHPコード作成
[サンプルコードはGithubに置きました](https://github.com/ryochessa/googleapi_sample)

### client_secret.jsonの配置
認証情報作成でダウンロードした**client_secret.json**をサーバーの直接アクセスできない場所に配置する。

### 認証を必要とするアクションで、Google認証画面にリダイレクトするようにする。
`$client->setRedirectUri()`で指定するURLは、Cloud Consoleでクライアントに設定した「承認済みのリダイレクトURI」の１つと一致していなければならない。
``` gmail/send_post.php
    $client = new Google\Client();
    $client->setAuthConfig('path to client_secret.json');
    $client->addScope(Google\Service\Gmail::GMAIL_SEND);
    
    // 認証後にgoogleから呼ばれるURIをセット。
	$client->setRedirectUri("https://" . $_SERVER['HTTP_HOST'] . '/callback/gmail.php');
    
	$client->setAccessType('online');
	$client->setIncludeGrantedScopes(true);
	$auth_url = $client->createAuthUrl();
	header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
    
```

### 認証後にGoogleから呼ばれるコールバックを作成
同意（認証）キャンセルの場合でも呼ばれる。$_GET['error']がセットされる。
``` callback/gmail.php
if(isset($_GET['error'])){
	echo("認証されませんでした。".$_GET['error']);
	return;
}   
```
同意があったときは$_GET['code']に認証コードがセットされる。APIに必要なのはアクセストークンである。アクセストークンに変換する。
``` callback/gmail.php
$accessToken = $client->fetchAccessTokenWithAuthCode($code);
```

このままメール送信できるのだが、このままだとURLに承認コードが表示されたままになってしまう。そこでアクセストークンをセッションに格納し、メール送信を実行するスクリプトにリダイレクトすることが推奨される。

``` callback/gmail.php
session_start();
$_SESSION['access_token'] = $accessToken;
header('Location: ' . filter_var("/gmail/send_exec.php", FILTER_SANITIZE_URL));
```
メール送信実行
``` callback/send_exec.php
$access_token = $_SESSION['access_token'];
$client->setAccessToken($access_token);
:メール作成
$message = new Google\Service\Gmail\Message();
$message->setRaw($rawMessage);

$gmail = new Google\Service\Gmail($client);
$gmail->users_messages->send("me", $message);

```

## 感想
Google Client APIはサイズが大きいし、コールバック等コードも複雑になるので、特定のアカウントからメールを送信するだけなら、アプリパスワードを使用する方が簡単。
今後DriveなどほかのAPIも使ってみたい。

## リンク
[Google API PHP client Github](https://github.com/googleapis/google-api-php-client)
[Gmail API RESTリファレンス](https://developers.google.com/gmail/api/reference/rest?hl=ja)
[Gamil API PHP doc](https://developers.google.com/resources/api-libraries/documentation/gmail/v1/php/latest/index.html)