Gmail api (gpc) OAuth認証でメールの送信

# 追記情報
1. googleが信頼性の低いアプリの使用を禁止したため、２段階認証によるメールの送信ができなくなった。
1. `yahoo.co.jp` のアカウントをつかってのメール送信もかなり制限されている。
1. OAuth認証をつかったmail apiは `Azure`, `Microsoft`,`Yahoo.com` などの大手もある。
1. 正直 `SendGrid` のほうが使いやすい。
    `SendGrid` は個人利用ができなくなった。
    審査も厳しくなり、途中でバンされたという報告が多数ある。
1. 独自ドメインの利用は Google Workspace(月額 680 円/ユーザー)で可能。
1. oauth認証のメールで送信し、差出人メールを`同じメールアカウント`で送ることで送信ドメイン認証もオールPASSになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/ac75ac25-0130-09f3-c622-4158bdb5e9cf.png)



# 参考サイト
ほぼ下の手順通り `2024/6/19` 少し変更がある。
大きな変更はテストユーザーでは不可になったのでユーザーにアカウント追加する必要がある。

https://www.webdesignleaves.com/pr/php/php_phpmailer.php

# 必要なもの
1. GCPのプロジェクトを作成して、gmail apiを有効にする
1. refresh token

# refresh tokenの取得
1. ドキュメントで公開されている [quickstart.phpで取得](https://github.com/googleworkspace/php-samples/blob/master/gmail/quickstart/quickstart.php) する方法
1. PHPMailerに付属している `get_auth_token.php`を使って取得する方法
  🙅　`get_oauth_token.php` にURLからアクセスするため 簡易サーバーの起動 は駄目
    なので、`Xampp` や `lamp` などの開発環境が必要
```php5:$ 簡易サーバーの起動
🙅　// php -S 127.0.0.1:80　簡易サーバーではリレフレシュトークンが取得できない。
```



# クライアントIDとクライアントシークレットの取得のための手続き

`gmail_api`はアプリ名
```:$ gmail_apiという作業ディレクトリを作成した
cd gmail_api
```
googleのリダイレクトURLの登録にPHPMailerの`get_auth_token.php`を使用するため
```:$
composer require phpmailer/phpmailer
```



[get_oauth_token.php にアクセス](http://localhost/gmail_api/vendor/phpmailer/phpmailer/get_oauth_token.php) 
`http://localhost/gmail_api/vendor/phpmailer/phpmailer/get_oauth_token.php`
このURLがリダイレクトURLになる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/0dc098b1-3c6b-d354-9968-455e7b251e2a.png)

[GCPでプロジェクトを作成する](https://console.cloud.google.com/welcome/)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/91d99aa1-b31f-4b98-3ee4-7909588e3934.png)
`gmail api` で検索をかけて 選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/931df073-c41f-7f02-a2e2-5a2cfac46267.png)
`gmail api` を有効にする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/2e1742b1-1be1-d6d6-dbe4-94bd6740101c.png)
`認証情報を作成`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/e83515e7-1844-ac4c-146c-08a9d2ec1cde.png)
まずは `OAuth同意画面` を作成する 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/0f79f9d9-dde3-73f5-57f4-fa539413dea7.png)
`add User`でuser名は アカウントのgmailアドレス

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/e877555c-f09e-bb43-55bd-5b4bb4190e10.png)
`Oauhtクライアント`を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/aaa50c05-08c9-06b4-062b-ec66b7192c70.png)
`http://localhost/vendor/phpmailer/phpmailer/get_oauth_token.php`を登録

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/b8205247-f836-7f54-a4eb-23fa8b536c87.png)
`クライアントID` と `クライアントシークレット`が取得できる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/1e3b4960-264c-593d-7f82-a6dc3804b2bf.png)

# PHPMailerでのリフレッシュトークンの取得
[get_oauth_token.php にアクセス](http://localhost/gmail_api/vendor/phpmailer/phpmailer/get_oauth_token.php) 
`http://localhost/gmail_api/vendor/phpmailer/phpmailer/get_oauth_token.php`
入力してcontinue
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/90b7fd22-5070-15a5-1d89-ef4c3c52a3c1.png)
２つくらい`continue`をおしてたら下記のサイトがでてくるのでチェックをいれてcontinue

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/bfe1f7a6-0352-e0cd-4a01-ad9799197359.png)

`refresh token: ここがリフレッシュトークン`
<font color="red">１度しか発行されないので注意</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/4e7cad55-bd1f-2f48-17a2-8fdf4b241d89.png)

# メールの送信
リフレッシュトークンを gmail apiになげてアクセストークンを取得することでメールが送れる。
アクセストークンの有効期限は１時間くらい。
## refresh tokenの期限切れ
Gmail APIのリフレッシュトークンは通常、期限切れにならない。
一度発行されたら期限が切れるまで再発行(再取得)されない。
以下のいくつかの特別な状況で期限切れになる可能性がある。
1. Gmail アカウントの所有者がアプリのアクセスを取り消した場合
1. リフレッシュトークンが6ヶ月間使用されていない場合


XOAUTH2 用のパッケージ（league/oauth2-google）をインストール
```:$
composer require league/oauth2-google
```
テストコード　

https://www.webdesignleaves.com/pr/php/php_phpmailer.php

webdesignleavesから丸パクリ 注意事項も `必読`
`$google_email`,`$clientId`,`$clientSecret`,`$refreshToken` は変更
```php5:index.php
<?php
// https://www.webdesignleaves.com/pr/php/php_phpmailer.php から丸パクリ

// HPMailer のクラスをグローバル名前空間（global namespace）にインポート
// スクリプトの先頭で宣言する必要があります
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\OAuth; //  ### 追加 ### 
 
// Alias the League Google OAuth2 provider class
use League\OAuth2\Client\Provider\Google;//  ### 追加 ### 
 
// Composer のオートローダーの読み込み（ファイルの位置によりパスを適宜変更）
require 'vendor/autoload.php';
 
//mbstring の日本語設定
mb_language("japanese");
mb_internal_encoding("UTF-8");
 
// インスタンスを生成（引数に true を指定して例外 Exception を有効に）
$mail = new PHPMailer(true);
 
//日本語用設定
$mail->CharSet = "iso-2022-jp";
$mail->Encoding = "7bit";
 
try {
  //サーバの設定
  $mail->SMTPDebug = SMTP::DEBUG_SERVER;  // デバグの出力を有効に（テスト環境での検証用）
  $mail->isSMTP();   // SMTP を使用
  $mail->Host       = 'smtp.gmail.com';  // ★★★ Gmail SMTP サーバーを指定
  $mail->SMTPAuth   = true;   // SMTP authentication を有効に
  $mail->AuthType = 'XOAUTH2';//  ### 追加 ###   AuthType を XOAUTH2 に指定
  //$mail->Username   = 'xxxxxx@gmail.com';  //  ### 不要 ### 
  //$mail->Password   = 'xxxxxx';  //  ### 不要 ###  
  // ★★★ 暗号化を有効に PHPMailer::ENCRYPTION_STARTTLS を使う場合はポートを 587 に
  $mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS; 
  $mail->Port = 465;  //ポートの指定
  
  // ###  OAUTH2 の設定  ### 
  //Gmail メールアドレス
  $google_email = 'ここ変更@gmail.com';
  //クライアント ID
  $clientId = 'ここ変更';
  //クライアントシークレット
  $clientSecret = 'ここ変更';
  //トークン（Refresh Token）
  $refreshToken = 'ここ変更';
 
  //OAuth2 プロバイダのインスタンスの生成 
  $provider = new Google(
    [
      'clientId' => $clientId,
      'clientSecret' => $clientSecret,
    ]
  );
 
  //OAuth プロバイダのインスタンスを PHPMailer へ渡す
  $mail->setOAuth(
    new OAuth(
      [
        'provider' => $provider,
        'clientId' => $clientId,
        'clientSecret' => $clientSecret,
        'refreshToken' => $refreshToken,
        'userName' => $google_email,
      ]
    )
  );
  // ###  OAUTH2 の設定 ここまで  ### 
 
  //受信者設定
  //差出人アドレス（Gmail のアドレス）, 差出人名 
  $mail->setFrom($google_email, mb_encode_mimeheader('差出人名'));  //  ### 変更 ### 
  // 送信先アドレス, 受信者名（受信者名はオプション）
  $mail->addAddress('ここ変更', mb_encode_mimeheader("受信者名")); 
  // 追加の受信者（受信者名は省略可能）
//   $mail->addAddress('xxxxxx@xxxxxx.com'); 
  //返信用アドレス（差出人以外に必要であれば）
  $mail->addReplyTo('info@example.com', mb_encode_mimeheader("お問い合わせ"));  
  //Cc 受信者の指定
//   $mail->addCC('xxxxxx@xxxxxx.com'); 
 
  //コンテンツ設定
  $mail->isHTML(true);   // HTML形式を指定
  //メール表題（タイトル）
  $mail->Subject = mb_encode_mimeheader('日本語メールタイトル');  
  //本文（HTML用）
  $mail->Body  = mb_convert_encoding('HTML メッセージ <b>BOLD</b>',"JIS","UTF-8"); 
  //テキスト表示の本文
  $mail->AltBody = mb_convert_encoding('テキストメッセージ',"JIS","UTF-8"); 
 
  $mail->send();  //送信
  echo 'Message has been sent';
} catch (Exception $e) {
  echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
}
```
# gmail apiでメールを取得
```:$
composer require google/apiclient:^2.0
```
```:$ .envファイルで読み込むため
composer require vlucas/phpdotenv
```
```php5:
<?php
require_once __DIR__ . '/vendor/autoload.php';

use Google\Client;
use Google\Service\Gmail;

$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

$clientId = $_ENV['CLIENT_ID'];
$clientSecret =  $_ENV['CLIENT_SECRET'];

$filePath = __DIR__ . '/gmail_refresh_token.txt';

if (is_readable($filePath)) {
    $refreshToken = file_get_contents($filePath);
} else {
    echo "ファイルが見つからないか、読み取りできません。";
}


$client = new Client();
$client->setClientId($clientId);
$client->setClientSecret($clientSecret);
$client->refreshToken($refreshToken);
$client->setScopes(['https://mail.google.com/']);

// Gmail サービスを作成
$service = new Gmail($client);

// 受信箱メッセージを取得
$optParams = [];
$optParams['maxResults'] = 5; // 例えば、最初の5件のメッセージだけを取得
$optParams['labelIds'] = 'INBOX'; // 受信箱のメッセージのみ


$messagesResponse = $service->users_messages->listUsersMessages('me', $optParams);

foreach ($messagesResponse->getMessages() as $message) {
    $messageId = $message->getId();
    $message_contents = $service->users_messages->get('me', $messageId, ['format' => 'full']);
    $body_parts = $message_contents->getPayload()->getParts();

    echo get_body($body_parts);
}

function get_body($parts) {
    $body = '';
    foreach ($parts as $part) {
    //$part['mimeType'] === 'text/plain'とかある
      if ($part['mimeType'] === 'text/html' && $part['body'] && $part['body']['data']) {
            $rawData = $part['body']['data'];
            $sanitizedData = strtr($rawData, '-_', '+/');
            $decodedMessage = base64_decode($sanitizedData);
            $body .= $decodedMessage;
        } elseif (!empty($part['parts'])) {
            $body .= get_body($part['parts']);
        }
    }
    return $body;
}
```