Gmail API を用いてメールの送受信を行うAndroidアプリケーションの実装例

カバー

[!] この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

Gmail、Google Drive、Twitter、Slackなどのサービスは外部連携のためのAPIを一般公開しており、 これらのAPIを利用することでより便利なアプリケーションを作成することができます。

これは、OpenID連携を用いた認証によって実現されますが、 以前、Webサービス間のID連携については、本ブログ OpenID ConnectによるID連携 で説明しました。 そこで今回は、アプリケーションとWebサービスのID連携、そして連携したWebサービスの利用方法を、Google Cloud Platformの設定例とアプリケーションの実装例とともにご紹介します。 なお、この記事で取り扱う情報は、2022年10月の情報を基にして執筆しています。

構成

この記事で紹介するシステムの構成は以下の通りです。
ここに画像

Google Cloud Platform の設定

本項ではID連携を行う認可サーバの設定、およびWebサービス(Gmail API)の有効化を説明します。

※ 注意事項 ※
Google Cloudコンソール(https://console.cloud.google.com/)にアクセスが必要なため、事前にアカウントを準備してください。

手順は以下の通りです。

  1. Google Cloud プロジェクトの作成
  2. Gmail APIの有効化
  3. OAuth同意画面の設定
  4. 認証情報の作成

1. プロジェクトの作成

まず最初にプロジェクトを作成します。

  1. Google Cloud コンソールにログインし、上部メニューのプロジェクトセレクタを選択します。
    プロジェクトセレクタ

  2. 「プロジェクトの選択」が表示されるので、「新しいプロジェクト」を選択します。
    ここに画像

  3. 「プロジェクト名」を設定して、作成を選択します。
    ここに画像

    • 「場所」については、特定の組織で利用したい場合に設定してください。
  4. 数分後、「プロジェクト」が作成されます。
    ここに画像

以上で、プロジェクトの作成は完了です。

2. Gmail APIの有効化

作成したプロジェクトで、Gmail APIが利用できるように有効化します。

  1. Google Cloud コンソールにログインし、上部メニューのプロジェクトセレクタを選択します。
    ここに画像
  2. 「プロジェクトの選択」が表示されるので、先ほど作成したプロジェクトを選択します。
    ここに画像
  3. 左のナビゲーションメニューから「 APIとサービス > 有効なAPIとサービス 」を選択します。
    ここに画像
  4. 「APIとサービス」の画面が表示されるので、「APIとサービスの有効化」を選択します。
    ここに画像
  5. APIライブラリの画面に遷移するので、「Gmail API」を検索します。
    ここに画像
  6. 検索結果から、Gmail APIを選択します。
    ここに画像
  7. 「有効にする」を選択します。
    ここに画像
  8. 選択後、すこし待つとGmail APIが有効化されます。
    ここに画像

以上で、Gmail APIの有効化は完了です。

3. OAuth同意画面の設定

「OAuth同意画面」とは、認証を行うことで取得される情報をアプリケーションの利用ユーザーに説明するために表示しなければならない画面です。 説明を読んだユーザーの承認により認証が行われます。

OAuth同意画面の設定は開発者が作成したプロジェクトごとに行います。

  1. Google Cloud Platformにログインし、上部メニューのプロジェクトセレクタを選択します。
    ここに画像
  2. 「プロジェクトの選択」が表示されるので、先ほど作成したプロジェクトを選択します。
    ここに画像
  3. 左のナビゲーションメニューから「 APIとサービス > OAuth同意画面 」を選択します。
    ここに画像
  4. アプリケーションの利用用途にしたがって外部、内部の選択をします。
    ここに画像
    • この記事では「外部」とします。
  5. 「OAuth 同意画面」について情報を入力して次に進みます。審査前の段階では必須項目の入力のみで問題ありません。
    ここに画像
    • アプリ名: 連携するアプリケーションの名前を入力
    • ユーザーサポートメール: ユーザーの問い合わせを受けるためのメールアドレスを入力
    • デベロッパーの連絡先情報/メールアドレス:開発者がGoogleからの連絡を受けるためのメールアドレスを入力

※ここでは、OAuth同意画面を利用するために必要な情報だけ入力しておきます。後で説明するアプリケーションを公開するための準備でアプリケーションの公開に必要な項目を入力します。

  1. 「スコープを追加又は削除」を選択して、Gmail APIのスコープを追加して次へを選択します。
    ここに画像 ここに画像
    • 今回はGmailの送受信を行うアプリケーションを作成するので gmail.readonly, gmail.send を選択

※一般公開するための審査で、スコープについての注意事項があります。これはこちらで説明します。

  1. 「テストユーザー」は追加せず次へを選択します。
    ここに画像

以上で、OAuth同意画面の設定は完了です。

4. 認証情報の作成

次に、認証情報を作成します。 この認証情報はアプリケーションに事前に組み込んで利用するものです。そのため、本手順の実施前に、Android アプリケーションのプロジェクトを作成しておく必要があります。

アプリケーションプロジェクトの作成後、以下の設定を実施してください。

  1. Google Cloud Platformにログインし、上部メニューのプロジェクトセレクタを選択します。
    ここに画像
  2. 「プロジェクトの選択」が表示されるので、先ほど作成したプロジェクトを選択します。
    ここに画像
  3. 左のナビゲーションメニューから「 APIとサービス > 認証情報 」を選択します。
    ここに画像
  4. 「認証情報を作成 > OAuthクライアントID」を選択します。
    ここに画像
  5. OAuthクライアントIDの作成 が表示されるので、以下の通り選択してください。
    ここに画像
    • アプリケーションの種類:Android
    • 名前:適当な識別名(管理画面で用途がわかるようにするためのもの)
    • パッケージ名:Android プロジェクトのパッケージ名を入力
    • SHA-1証明書のフィンガープリント: Android アプリケーションの ※SHA-1値 を設定

※ SHA-1値は以下のコマンドで取得します。

keytool -keystore path-to-debug-or-production-keystore -list -v
  1. 作成されるとクライアントIDが発行されます。発行されたクライアントIDはJSON形式でダウンロードできます。
    ここに画像

ダウンロードしたJSONファイルには、クライアントID以外にもID認証で利用するパラメータが含まれています。

{
"installed":{
"client_id":"aaabbbcccddd-eeefffggghhhiiijjjkkk.apps.googleusercontent.com",
"project_id":"oauth2-Gmail-sample",
"auth_uri":"https://accounts.google.com/o/oauth2/auth",
"token_uri":"https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs"
}
}

以上で、認証情報の作成は完了です。

Android アプリケーションの作成

前節でGmail APIの有効化および認証情報(クライアントID)の作成を行いました。 次に本節ではメールの送受信を行うアプリケーションを作成します。

アプリケーションでは以下の処理を順に実装します。

  1. 認証情報およびアクセストークンの取得
  2. メール閲覧/送信のリクエスト

1. 認証情報およびアクセストークンの取得

まずは、認証情報およびアクセストークンの取得を行います。 いくつか方法がありますが、この記事ではAppAuthライブラリを用いた手法をご紹介します。※1

1-1. AppAuthライブラリの導入

Android版のAppAuthライブラリは、MavenCentralリポジトリから取得することができます。 プロジェクトのbuild.bundleに以下を記述して、AppAuthライブラリをダウンロードしてください。

dependencies {
implementation 'net.openid:appauth:<version>'
}

1-2. 認証情報とアクセストークンの取得

次に、AppAuthライブラリを用いて認可サーバから認証情報とアクセストークンを取得する手順について説明します。

トークンを取得するための処理は以下の順に実装します。

  1. OAuth認証を行うサインインボタンをユーザーに表示
  2. ユーザーによるサインインボタンの押下をトリガーにOAuth同意画面をユーザーに表示
  3. ユーザーによる同意画面の承諾をトリガーとして、認可サーバから認証情報を取得
  4. 認証情報を用いて認可サーバからアクセストークンを取得
  5. 取得したアクセストークンをファイルに保存

次に示すサンプルコードにて、 上記1~5にあたる処理を記載していますので参考にしてください。

認証情報、アクセストークン取得(サンプルコード)
// ボタン等
private ImageButton mYesButton = null;
// スコープ
private String Gmail_SCOPE = "https://www.googleapis.com/auth/Gmail.readonly https://www.googleapis.com/auth/Gmail.send"
// クライアントID情報
private String Gmail_CLIENT_ID = "aaabbbcccddd-eeefffggghhhiiijjjkkk.apps.googleusercontent.com";
private Uri REDIRECT_URI = Uri.parse("com.googleusercontent.apps.aaabbbcccddd-eeefffggghhhiiijjjkkk:/oauth2redirect");
private Uri Gmail_AUTH_URI = Uri.parse("https://accounts.google.com/o/oauth2/v2/auth");
private Uri Gmail_TOKEN_URI = Uri.parse("https://oauth2.googleapis.com/token");
// OAuth認証関連
private int RC_AUTH = 100;
private final AtomicReference<AuthorizationRequest> mAuthRequest = new AtomicReference<>();
private final AtomicReference<CustomTabsIntent> mAuthIntent = new AtomicReference<>();
private AuthorizationServiceConfiguration serviceConfig =
new AuthorizationServiceConfiguration(
Gmail_AUTH_URI, // 認証エンドポイント
Gmail_TOKEN_URI); // トークンエンドポイント
private AuthState authState = new AuthState(serviceConfig);
private AuthorizationService mAuthService;
private AuthorizationRequest.Builder authRequestBuilder =
new AuthorizationRequest.Builder(
serviceConfig, // 認証サービスの構成
Gmail_CLIENT_ID, // クライアントID。通常は事前に設定された静的なもの。
ResponseTypeValues.CODE, // response_type の値:コードが必要です。
REDIRECT_URI); // 認証レスポンスの送信先となるリダイレクト URI
@NonNull
private BrowserMatcher mBrowserMatcher = AnyBrowserMatcher.INSTANCE;
public void onCreate(){
// ボタン等の取得
mYesButton = (ImageButton)findViewById(R.id.yesButton);
// Google サインインボタンのリソース取得
// サインインボタンは、Googleのガイドラインに従って作成してください。
// https://developers.google.com/identity/branding-guidelines
mYesButton.setBackgroundResource(R.drawable.google_btn_selector);
// ボタンイベント
mYesButton.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
buttonPush("", v, true);
doOAuth();
}
});
}
private void doOAuth(){
// 認証設定の準備
AppAuthConfiguration.Builder mAACbuilder = new AppAuthConfiguration.Builder();
mAACbuilder.setBrowserMatcher(mBrowserMatcher);
mAACbuilder.setConnectionBuilder(DefaultConnectionBuilder.INSTANCE);
mAuthService = new AuthorizationService(this, mAACbuilder.build());
// 認証リクエストの作成
mAuthRequest.set(authRequestBuilder.setScope(Gmail_SCOPE).build());
CustomTabsIntent.Builder intentBuilder =
mAuthService.createCustomTabsIntentBuilder(mAuthRequest.get().toUri());
mAuthIntent.set(intentBuilder.build());
// OAuth同意画面をブラウザで開く
Intent intent = mAuthService.getAuthorizationRequestIntent(
mAuthRequest.get(),
mAuthIntent.get());
startActivityForResult(intent, RC_AUTH);
}
// OAuth同意画面でユーザーが操作した結果を受け取る
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode,resultCode,data);
if(requestCode == RC_AUTH){
AuthorizationResponse resp = AuthorizationResponse.fromIntent(data);
AuthorizationException ex = AuthorizationException.fromIntent(data);
if (ex == null && resp != null){
// 認証成功
authState.update(resp,ex); //認証情報を更新
tokenRequestAndUpdate(resp); // アクセストークンの取得
} else {
// 認証失敗
if (ex == null) {
// 認証失敗 (レスポンス無し発生)
} else {
// 認証失敗 (例外発生)
if (ex.type == AuthorizationException.TYPE_GENERAL_ERROR &&
ex.code == AuthorizationException.GeneralErrors.USER_CANCELED_AUTH_FLOW.code) {
// ユーザー操作によってキャンセルされた場合
} else {
// 認証処理中に何らかのエラーが発生した場合
}
}
}
} else {
// リクエストコードの整合性が取れていないので認証失敗
}
}
// アクセストークンの取得
private void tokenRequestAndUpdate(AuthorizationResponse resp){
mAuthService.performTokenRequest(resp.createTokenExchangeRequest(), new AuthorizationService.TokenResponseCallback(){
@Override public void onTokenRequestCompleted(
TokenResponse tResp, AuthorizationException tEx) {
if (tResp != null && tEx == null) {
// 交換に成功
authState.update(tResp,tEx); // アクセストークンを更新
saveTokenFile(authState) // アクセストークンを含む認証情報をファイルに保存
} else {
if (tEx == null) {
// 認証失敗 (レスポンス無し発生)
} else {
// 認証失敗(何らかのエラー)
}
}
}
});
}
// アクセストークンをファイル(JSON)に保存
public boolean saveTokenFile(AuthState mAuthState){
String filePath =// 保存先
synchronized(fileSync) {
// JSON文字列化する
JSONObject tokenJson = mAuthState.jsonSerialize();
// ファイルとして書き込む
FileUtil.writeFile(tokenJson.toString(), filePath)
}
}

以上で、認証情報およびアクセストークンの取得について説明は終わりです。

2. メール閲覧/送信のリクエスト

Googleが公開する Gmail APIのライブラリを用いて実装を行うため、 プロジェクトのbuild.bundleに以下を記述して、Gmail APIのライブラリをダウンロードしてください。

dependencies {
api 'com.google.api-client:google-api-client-android:<version>'
api 'com.google.oauth-client:google-oauth-client-jetty:<version>'
api 'com.google.apis:google-api-services-Gmail:<version>'
api 'com.google.auth:google-auth-library-oauth2-http:<version>'
}

2-1. メールを閲覧する

メールの閲覧は以下の実装例を参考にしてください。

  1. 事前に取得したアクセストークンの読み込み
  2. 閲覧するメールのラベルを指定(例、INBOX:受信フォルダ、SENT:送信済みフォルダを指す)
  3. 指定したラベルのメッセージリストを取得
  4. メッセージIDごとにメール本文を取得
メール閲覧(サンプルコード)
String user = sampleuser // ユーザー名を設定
// フォイルに保存されているトークン情報の読み込み
String tokenFilepath = ;
String jsonStr = FileUtil.readFile(tokenFilepath);
JSONObject jsonObject = new JSONObject(jsonStr);
AuthState tokenData = AuthState.jsonDeserialize(jsonObject);
// 認証情報の初期化
AccessToken accessToken = new AccessToken(
tokenData.getAccessToken(),
tokenData.getAccessTokenExpirationTime());
GoogleCredentials credentials = GoogleCredentials.create(accessToken);
HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(credentials);
// Gmail API クライアントの生成
Gmail service = new Gmail.Builder(new NetHttpTransport(),
GsonFactory.getDefaultInstance(),
requestInitializer).setApplicationName("アプリケーション名").build();
// 取得するメール種別(ラベル)を指定
List<String> idLabels = new ArrayList<>();
idLabels.add("ラベル名");
// メッセージリストの取得
ListMessagesResponse listmessageResponse = service.users().messages().list(user).setLabelIds(idLabels).execute();
List<com.google.api.services.Gmail.model.Message> GmailMessages = listmessageResponse.getMessages();
// メールの本文の取得
for (com.google.api.services.Gmail.model.Message message : GmailMessages) {
// 1件ずつ本文を取得
com.google.api.services.Gmail.model.Message fullMessage = service.users().ssages().get(user, tGmailMessageID).execute();
}

2-2. メールを送信する

メールの送信は以下の実装例を参考にしてください。

  1. 事前に取得したアクセストークンを読み込む
  2. 送信するメールメッセージを、MimeMessageクラスのメッセージとして作成する
  3. 作成したMimeMessageクラスのmessageをGmailメッセージの形に変換する
  4. Gmailクライアントからメッセージを送信する
メール送信(サンプルコード)
// フォイルに保存されているトークン情報の読み込み
String tokenFilepath = ;
String jsonStr = FileUtil.readFile(tokenFilepath);
JSONObject jsonObject = new JSONObject(jsonStr);
AuthState tokenData = AuthState.jsonDeserialize(jsonObject);
// 認証情報の初期化
AccessToken accessToken = new AccessToken(
tokenData.getAccessToken(),
tokenData.getAccessTokenExpirationTime());
GoogleCredentials credentials = GoogleCredentials.create(accessToken);
HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(credentials);
// Gmail API クライアントの生成
Gmail service = new Gmail.Builder(new NetHttpTransport(),
GsonFactory.getDefaultInstance(),
requestInitializer).setApplicationName("アプリケーション名").build();
// メッセージの作成
MimeMessage msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(data.from));
InternetAddress[] address = InternetAddress.parse(data.to);
msg.setRecipients(Message.RecipientType.TO, address);
msg.setSubject(data.subject, Constants.CHARA_SET);
msg.setSentDate(new Date());
// 作成したメッセージをGmail メッセージ形式に変換
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
msg.writeTo(buffer);
byte[] rawMessageBytes = buffer.toByteArray();
String encodedEmail = new String(Base64.encodeBase64(rawMessageBytes));
com.google.api.services.Gmail.model.Message message = new com.google.api.services.Gmail.model.Message();
message.setRaw(encodedEmail);
// Gmailメッセージの送信
message = service.users().messages().send(user, message).execute();

アプリケーションを公開するための準備

作成したAndroidアプリケーションを多くの方に安全に使用していただくためにOAuth同意画面の審査を受けましょう。

アプリケーションを個人や特定の組織内でのみ利用する場合には必ずしも審査を受ける必要はありませんが、以下のような制限があります。

  • OAuth認証を利用できるユーザーの上限が100人まで
  • OAuth同意画面が表示される際、「このアプリケーションはGoogleに確認されていない」というメッセージが表示されてしまう

審査は、設定画面のすべての項目に設定/回答し、Google Cloud Platform を通じてGoogleの担当者に確認を依頼します。 審査結果は、OAuth同意画面の設定で設定した「デベロッパーのメールアドレス」に英文で連絡がありますが、日本と時差があるためか、当社の場合は18時までに依頼すると、翌日11時には連絡があることが比較的多いように感じました。

審査は、指摘を受け修正して再審査を依頼して次の指摘を受けるということを繰り返したので、 おおよそ1~2週間程度の期間を見ておくとよいです。

次の節からは、OAuth同意画面の審査項目の中で、特に指摘を受けやすいポイントをOAuth API verification FAQsと当社ノウハウを交えて説明します。

アプリケーションのドメイン

アプリケーションのドメインには、以下3件を設定します。

  1. アプリケーションのホームページ (必須項目)
  2. プライバシーポリシー(必須項目)
  3. 利用規約

上記のうち必須項目である「アプリケーションのホームページ」と「プライバシーポリシー」については、OAuth API verification FAQsで詳しい注意点が記載されています。

以下、引用(和訳)です。

- ホームページの注意点
- サインインを必要とせず、誰でも閲覧可能な状態で公開されている必要があります。
- アプリケーションが提供する機能のうち、APIを利用すること機能について、その利用用途を明確に記載する必要があります。
- Google Play ストアや Facebook へのリンクは、ホームページとは見なされません。
- プライバシーポリシーの注意点
- アプリケーションが Google ユーザー データにアクセス、使用、保存、または共有する方法を開示する必要があります。 これは、もれなく実施する必要があるので、プライバシーポリシーとアプリケーションの動作が必ず一致するようにしてください。

加えて、アプリケーションが「制限されたスコープを利用する場合」は、アプリケーションがGoogle API Services User Data Policyおよび追加要件を遵守することを明記しなければなりません。

以下、引用(和訳)です。

アプリケーションがGoogle API の利用規約、Google のAPI サービス ユーザー データ ポリシー、および特定の範囲の追加要件 に準拠していることを確認してください。これには、アプリケーションが制限された範囲の Google ユーザー データにアクセスする場合の年次セキュリティ評価の実施が含まれます。

承認済みドメイン

承認済みドメインには、アプリケーションのホームページやプライバシーポリシーが公開されているドメインのルートドメインを設定する必要があります。ここでサブドメインの設定はできませんので注意してください。 また、設定するドメインはGoogle Search Consoleで所有権が認められているドメインに限られているため、事前に設定する必要があります。

たとえば、Webページが以下のような階層構造だとします。 この場合は、ルートドメインであるhttps://company.comがGoogle Search Consoleで所有者となるように設定したうえで、承認済みドメインに設定します。

https://company.com ・・・ 企業Webページ(ルートドメイン)
|
+---https://seihin.company.com ・・・ 製品ページ(サブドメイン)
|
+---https://seihin.company.com/privacy ・・・ 製品のプライバシーポリシー

Google Search Consoleについては、ヘルプページも参考にしてください。

スコープ

アプリケーションが利用するAPIのスコープを設定します。 ここで、スコープとは要求するリソースにアクセスできる権限を表現したものです。

例えば、Gmail API で利用可能なスコープは全部で10種類ありますが、基本的に利用するスコープの範囲は必要最低限であることが求められます。

メールの送受信のみを機能として利用する場合、 スコープの観点から見ると以下の2つのパターンが利用できます。

  1. https://mail.google.com/(メールに関するフルアクセス権限)
  2. https://www.googleapis.com/auth/gmail.send(メール送信権限)とhttps://www.googleapis.com/auth/gmail.readonly(メール閲覧権限)の組み合わせ

上記のどちらでもメールの送受信を行うことは可能ですが、 1は、メールの送受信を行うアプリケーションに対しては過剰に広いスコープであると見られるため、 基本的には最小限のスコープを使用するように努めてください。 もし、どうしてもこのスコープが必要だという場合はGoogleの審査官が理解できるよう正確に説明しましょう。

デモ動画

アプリケーションが「制限されたスコープを利用する場合」は、そのアプリケーションの動作を説明するデモ動画を作成してGoogleにその動画を審査してもらう必要があります。 デモ動画はアプリケーションの動作を撮影してYouTubeにアップロードし、そのURLを回答します。

OAuth API verification FAQs では、デモ動画の注意点が記載されています。

以下、引用(和訳)です。

- ユーザーが経験する OAuth 付与プロセスを英語で示します (同意フロー、および Google ログインを使用している場合はログイン フロー)。
- OAuth 同意画面にアプリケーション名が正しく表示されることを示します。
- OAuth 同意画面の URL バーにアプリケーションのクライアント ID が正しく含まれていることを示します。
- 注:これは、ネイティブの Android および iOS アプリケーションでは必要ありません。
- ※リクエストした機密性の高い制限付きの各スコープによって有効になる機能を示すことで、データがどのように使用されるかを示します。
- 複数のクライアントを使用しているため、複数のクライアント ID がある場合は、各 OAuth クライアントでデータがどのようにアクセスされるかを示します。

特に注意すべきなのは、※の項目です。
各スコープによって得られるデータの使用方法について、デモ動画(字幕含む)で説明する必要がありますが、その使用方法を理解してもらえなければ審査が進みません。
また、使用方法がわかりにくい場合は、審査によって当該スコープの使用が認められない可能性があります。
Googleの審査の結果が通知される際に、アドバイス(別スコープの提案など)を受けますが、自分が意図しない解釈をされてしまった場合は、必ず訂正のメッセージを送付して先方と認識を合わせるようにしましょう。

おわりに

この記事ではID連携の実装例として、 Gmail API を用いてメールの送受信を行うAndroidアプリケーションの実装例を説明しました。 また、技術的な部分だけでなく、GoogleによるOAuth同意画面の審査のポイントについても説明しました。

外部サービスと連携することで、アプリケーションの機能はより一層便利になります。 ただし外部サービスとの連携では技術仕様だけでなく、利用規約を遵守し、使用範囲やアクセス頻度など適切な利用を心がけましょう。

この記事で紹介したサービスの連携は比較的簡単なものではありますが、アプリケーション開発者の方々の参考となれば幸いです。

脚注

※1 モバイル &デスクトップ アプリ向け OAuth 2.0


TOP
アルファロゴ 株式会社アルファシステムズは、ITサービス事業を展開しています。このブログでは、技術的な取り組みを紹介しています。X(旧Twitter)で更新通知をしています。