Android
OAuth
アプリ
GoogleDrive
REST-API

「Google Drive REST API v3」に「アクセストークン」を付与してファイルを検索/ダウンロードする

背景

ちょっと思いついたので、最後の「疑問」について、もし、情報を戴けるなら、大変に有り難い。

「アクセストークン」を取得する

アクセストークン」の取得方法は、本題ではないので、本記事では割愛する。

開発者でも機会がないと「アクセストークン」そのものがよく分からないという人も多い(珍しくないことだと思っている)。

こちらの記事「一番分かりやすい OAuth の説明」が本当に分かりやすく、タイトル詐欺ではないので、よく分からないという方に是非オススメしたい。

「アクセストークン」はユーザ毎に発行されるものなので、ある意味では「個人を特定するID」とも言えるかもしれない。

だから、「アクセストークン」の取り扱いは、本当に重要だ。

「Google Drive」から「ファイル一覧」を取得する

公式リファレンス「Files: list」に記載されているAPIはとても単純だ。

GET https://www.googleapis.com/drive/v3/files

これに、個人を識別する「アクセストークン」を付与するだけで、「Google Drive」から「ファイル一覧」を取得できる、ということだ。

公式リファレンス「Standard Query Parameters」を確認すると、「アクセストークン」のパラメータは、「access_token」だ。つまり、

GET https://www.googleapis.com/drive/v3/files?access_token=ユーザ毎のアクセストークン

こうだ。

「REST API」なので、勿論、ブラウザでアクセスするだけで「ファイル一覧」が取得できるということだ。

例えば、アクセストークンを付与せず、

GET https://www.googleapis.com/drive/v3/files?access_token=

この状態でブラウザからアクセスすると、

{
 "error": {
  "errors": [
   {
    "domain": "usageLimits",
    "reason": "dailyLimitExceededUnreg",
    "message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.",
    "extendedHelp": "https://code.google.com/apis/console"
   }
  ],
  "code": 403,
  "message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup."
 }
}

ちゃんと「403」のエラーがJSON形式でブラウザに表示される。

では、正しい「アクセストークン」を付与してみよう。

{
 "kind": "drive#fileList",
 "incompleteSearch": false,
 "files": [
  {
   "kind": "drive#file",
   "id": "13JJJJJJJJJJO7SSS1iiiS4BTAAAAA0xxx",
   "name": "eros.mp4",
   "mimeType": "video/mp4"
  }
 ]
}

これは勿論、例だが、「files」に「ファイル一覧」が表示される。上記の場合は、「動画ファイル(video/mp4)」が「ひとつ」だけ、「Google Drive」に格納されていた、ということになる。

……ところで、これ、マジ怖くね?

たった、これだけで、ドライブ内の「全ファイル一覧(JSON)」が取得できてしまう。

「アクセストークン」が、どれだけ重要な個人情報であるか、分かるだろうか?

もし自分の「アクセストークン」が誰かに盗まれてしまったら、ここに記載できないようなあんな動画やこんな画像が、ブラウザからアクセスするだけで、盗んだその人に簡単に丸見えになってしまう。もしもストーカーだったなら、愛しいあの人のアクセストークン、喉から手が出るくらい、ほしいでしょう。

ファイルをダウンロードする

先程の「ファイル一覧(JSON)」に含まれる情報に、「ファイルID」というものがある。

{
 "kind": "drive#fileList",
 "incompleteSearch": false,
 "files": [
  {
   "kind": "drive#file",
   "id": "13JJJJJJJJJJO7SSS1iiiS4BTAAAAA0xxx",
   "name": "eros.mp4",
   "mimeType": "video/mp4"
  }
 ]
}

id」が「ファイルID」を示している。つまり、この例だと、「13JJJJJJJJJJO7SSS1iiiS4BTAAAAA0xxx」が「ファイルID」そのものだ。

「Google Drive」からファイルをダウンロードする場合、この「ファイルID」を指定して行う。

公式リファレンス「Files: get」を確認しよう。

GET https://www.googleapis.com/drive/v3/files/fileId

私にはこれは「」だったのだが、これに「アクセストークン」を付与するだけでは、ダウンロードができなかった。

別途、公式リファレンス「Download Files」に、ダウンロード方法が記載してある。

一緒に書いておいてくれよ......。

要するに、総合すると、以下のようになる。「アクセストークン」を付与するだけではなく、「alt=media」が必要だ。

GET https://www.googleapis.com/drive/v3/files/fileId?alt=media&access_token=ユーザ毎のアクセストークン

※「fileId」部分はこの例だと「13JJJJJJJJJJO7SSS1iiiS4BTAAAAA0xxx」に置き換えること

「フォルダ」を検索する

通常、「Google Drive」をよく利用する人なら、フォルダを設けて整理しているのではないだろうか。

「Google Drive」のある特定の「フォルダ」を検索することも、勿論、可能だ。

公式リファレンスを読んでいても本当にしばらく分からなかったのだが、「フォルダもファイル扱い」だ。

以下は先程の「ファイル一覧(JSON)」だが、

{
 "kind": "drive#fileList",
 "incompleteSearch": false,
 "files": [
  {
   "kind": "drive#file",
   "id": "13JJJJJJJJJJO7SSS1iiiS4BTAAAAA0xxx",
   "name": "eros.mp4",
   "mimeType": "video/mp4"
  }
 ]
}

mimeType」を確認して、フォルダなのかどうかを判断する。

公式リファレンス「Supported MIME Types」を確認すると、フォルダの「mimeType」は、「application/vnd.google-apps.folder」だ。

では、目当てのフォルダを検索してみよう。

公式リファレンス「Search for Files and Team Drives」に則って、「Examples」でクエリの記述方法も確認しながら、フォルダ名を部分一致(contains)で検索するならば、以下のようになる。

GET https://www.googleapis.com/drive/v3/files?access_token=ユーザ毎のアクセストークン&q=mimeType=%27application/vnd.google-apps.folder%27+and+name+contains+%27検索したいフォルダ名の一部文字列%27

これで、クエリに該当する「フォルダ一覧(=ファイル一覧という扱い)」が取得できる。

特定のフォルダ内のファイル一覧を取得する

公式リファレンス「Search for Files and Team Drives」の「Examples」に書いてあった。

特定のフォルダ内」というのは、各ファイルの情報に「ペアレント(=親フォルダ)」情報として格納されている。

GET https://www.googleapis.com/drive/v3/files?access_token=ユーザ毎のアクセストークン&q=%27フォルダID(=ファイルID)%27+in+parents

in parents」で「フォルダID(=ファイルID)」を指定してクエリを構成すると、その「フォルダID(=ファイルID)」が「ペアレント(=親フォルダ)」に含まれているファイル一覧だけが取得できる。

一度に全てを取得できなかったら

ファイルが山程あると、一度に取得できないということが起きる。

その場合、JSONには、「nextPageToken」パラメータが含まれるようになる。

{
 "kind": "drive#fileList",
 "nextPageToken": "~~~AIAIAIR2LTlQLci44eZ75EsXgb9dIV4HXTHpFsVY87Behv0EFTwteD8cEpgqVBj4MD9YBG8SB_XZ54lcmhYs2ztlcdUkKeH2E6mRscOO7IMP4tTXoY5fBNcEJtsL12cAg8idnB0IExqfayIA-00_FtN1dmnoFPxJhrAjmGyra-arZBynXQrLztyYdrmnNegnG94nefyvYg5JUfrY86phlk6HTLBP-HMQQh0QxP6yIn9ksc_zyWBQrfA_iHs6xkzu7TLr9KL4SDIpizSq_goVgVnJq5lH_6sNgZNeVVVVdhhhh_ccJXR_0KgPUB15P4A-bK41xdp-SaZ5S6CkzMx9Su6Z91nbPYmcRnu027TNLgtzBwUQ6pERU0QgndvhG4aP5zPL1ekkkkkk",
 "incompleteSearch": false,
 "files": [

「nextPageToken」は、全て取得完了していたら含まれることはないので、このパラメータの有無で判断するだけでOKだ。

次のリクエスト時に、「nextPageToken」を付与すれば、続きが取得できる。

GET https://www.googleapis.com/drive/v3/files?access_token=ユーザ毎のアクセストークン&pageToken=付与されていたネクストトークン

疑問

こちらの記事「一番分かりやすい OAuth の説明」が本当に分かりやすかったのだが、「アクセストークン」は、「クライアントアプリケーション」に渡されていた。

これは当然なのだが、その後、「クライアントアプリケーション」から「全く関係のない別のアプリ」「全く関係のない別のサーバー」へ渡すことが可能で、そこに物理的な制限はない

OAuthの思想からすると、これはやるべきではない、やってはいけない、ことのように感じるのだが、実態はどうなのだろうか?

例えば、Androidアプリ上でアカウント認証して、「アクセストークン」をAndroidアプリは得たとしよう。

この「アクセストークン」を外部サーバへネットワーク経由で渡してしまえば、そのサーバ上でさも認証したかのように、「Google Drive」から自由に情報を取得することが可能だ。

同様に、Androidアプリから、別のAndroidアプリへ共有データとして「アクセストークン」を渡してしまうことも可能で、別のAndroidアプリが「Google Drive」から自由に情報を取得することが可能だ。

しかも、そうしていることが、ユーザには分からない

素朴に、これは「スパイウェア」もどきに感じてしまったのだが、悪いことしないからという前提でこういうことやっているサービス、予想だが、結構、ないのだろうか?

例えばこんなアプリをGoogleやAppleにリリースしたら、許されるの?

今回、「REST API」を改めて考えてみて、素朴に、疑問に思った。「Google Drive」にアクセスするアプリを何も考えずに使っているけど、実はすげえ怖いことなんじゃないかって。

どなたかご存知だったら是非ご教授願いたい。

関連記事

参考記事