
クライアントアプリからGoogle Apps APIを使用し、Google CalendarやGoogle Driveなどを利用するには、OAuth 2.0で認証を行う必要があります。OAuth 2.0の仕組みやOAuth 2.0で認証を行うプログラミングの方法について解説します。
目次
OAuth 2.0とは
OAuth 2.0 は、サードパーティーアプリによるHTTPサービスへの限定的なアクセスを可能にする認可フレームワークである。
[参考]The OAuth 2.0 Authorization Framework
OAuth 2.0の目的
従来のクライアントサーバー型の認証モデルでは、クライアントはリソースオーナーのクレデンシャル(IDやパスワード)を使ってサーバーに対して認証を行い、サーバー上の保護されたリソースにアクセスする。つまり、サードパーティーアプリに保護されたリソースへのアクセス権を与えるには、リソースオーナーは自身のクレデンシャル(ID/パスワード)をサードパーティーと共有する必要があるり、これはいくつかの問題と制限をもたらす。
- サードパーティーアプリは、後の利用のためにリソースオーナーのクレデンシャル(ID/パスワード)を保存しておかなければならない。特にパスワードは平文 で保存されることになる。
- パスワードを利用することでセキュリティが低下したとしても、サーバーはパスワードベースの認証方式をサポートしなければならない。
- サードパーティーアプリは、リソースオーナーの保護されたリソースに対して過度に広範囲のアクセス権を得ることになる一方で、リソースオーナーは、アクセスをリソースのサブセットに限定したりアクセス可能期間を制限したりできない。
- リソースオーナーは各サードパーティーごとにアクセス権を無効化することはできず、アクセス権を無効化する際にはすべてのサードパーティーが持つアクセス権を無効化しなければならない。つまりそれはパスワード変更を意味する。
- サードパーティーアプリの情報漏えいはエンドユーザーのパスワードおよびパスワードによって保護されているすべての情報の漏洩につながる。
OAuthは、認可レイヤーをもうけてクライアントとリソースオーナーの役割を分けることで、これらの問題の解決に取り組む。OAuthでは、クライアントは、リソースオーナーのコントロール下にありリソースサーバーによってホストされているリソースへのアクセス権を要求する。そしてリソースオーナーのクレデンシャル(ID/パスワード)そのものとは別のクレデンシャル(アクセストークン)を取得する。
OAuth 2.0の登場人物
OAuthは以下の4つのロールを定義している。
- リソースオーナー (resource owner)
保護されたリソースへのアクセスを許可するエンティティー、リソースオーナーが人間の場合はエンドユーザーと呼ばれる。
- リソースサーバー (resource server)
保護されたリソースをホストし、アクセストークンを用いた保護されたリソースへのリクエストを受理してレスポンスを返すことのできるサーバーである。
- クライアント (client)
リソースオーナーの認可を得て、リソースオーナーの代理として保護されたリソースに対するリクエストを行うアプリケーションである。 (OAuthクライアントは、サーバー上で動くアプリであることもあれば、デスクトップアプリやその他のデバイス上で動くアプリであることもある)
- 認可サーバー (authorization server)
リソースオーナーの認証とリソースオーナーからの認可取得が成功した後、アクセストークンをクライアントに発行するサーバーである。
OAuth 2.0の仕組み
OAuth 2.0 認証のシーケンス図を以下に示します。
Google Apps APIの認証情報を取得する
使用するAPIの認証情報を取得する手順を以下に示します。
使用するGoogle Apps APIを有効にする
Google Cloud Platform コンソールのAPI Managerで、プロジェクトで使用するAPIを有効にします。
- 使用するAPI(ここでは、Google Drive API)をクリックします。
Google Drive APIは、アプリからユーザーデータへアクセスすることになるので、「OAuth 2.0によるユーザーデータへのアクセス」に該当します。
- 「有効にする」をクリックします。
Google Drive APIでアクセスするための認証情報を作成していきます。
- 「認証情報を作成」をクリックします。
プロジェクトへの認証情報を追加する
プロジェクトへの認証情報の追加を行っていきます。(以前の画面をショートカットし、この画面からも始められます)
必要な認証情報の種類を調べる
まずは、どんな認証情報が必要かを指定します。
- 「使用するAPI」を選択します。(以前の画面で指定したGoogle Drive APIが選択されています)
- 「APIを呼び出す場所」を指定します。ここでは、「その他のUI(Windows, CLIツールなど)」を選択します。
- 「アクセスするデータの種類」を選択します。ここでは「ユーザーデータ」を選択します。
- 「必要な認証情報」をクリックします。
OAuth 2.0 クライアントIDを作成する
- アプリケーションIDの元になる「名前」を入力します。
OAuth 2.0 同意画面を設定する
- プロバイダの「メールアドレス」を入力します。
- 「ユーザに表示するサービス名」(何のサービスから認証を求められているかを識別できる名前)を入力します。
- 「次へ」をクリックします。
認証情報をダウンロードする
- 「ダウンロード」をクリックします。(後でダウンロードすることもできます)
- 認証情報ファイル「client_id.json」がダウンロードされます。
- 「完了」ボタンをクリックします。
Google Apps APIを使用したPythonクライアントアプリを作成する
Google Apps APIを使用した、アプリのプログラミングの方法について示します。
環境:Python 3.6.1
Google Client Library をインストールする
Google Apps APIを使用するには、Google Client library をインストールします。
$ pip install –upgrade google-api-python-client |
Pythonソースコードの実行時に「ImportError: cannot import name ‘discovery’」のエラーが出力される場合は、以下のコマンドでGoogle Client Libraryを再インストールしてみてください。
pip install –force-reinstall google-api-python-client |
OAuth 2.0 プログラミング
以下に、OAuth 2.0 のクレデンシャル(アクセストークン)を取得するPythonソースコードを示します。
from __future__ import print_function import httplib2 import os from apiclient import discovery from oauth2client import client from oauth2client import tools from oauth2client.file import Storage try: import argparse flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args() except ImportError: flags = None # If modifying these scopes, delete your previously saved credentials # at ~/.credentials/google-drive-credential.json SCOPES = 'https://www.googleapis.com/auth/drive.metadata.readonly' CLIENT_SECRET_FILE = 'client_secret.json' APPLICATION_NAME = 'Google Drive List Files' def get_credentials(): """Gets valid user credentials from storage. If nothing has been stored, or if the stored credentials are invalid, the OAuth2 flow is completed to obtain the new credentials. Returns: Credentials, the obtained credential. """ home_dir = os.path.expanduser('~') credential_dir = os.path.join(home_dir, '.credentials') if not os.path.exists(credential_dir): os.makedirs(credential_dir)credentials credential_path = os.path.join(credential_dir, 'google-drive-credential.json') store = Storage(credential_path) credentials = store.get() if not credentials or credentials.invalid: flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES) flow.user_agent = APPLICATION_NAME if flags: credentials = tools.run_flow(flow, store, flags) else: # Needed only for compatibility with Python 2.6 credentials = tools.run(flow, store) print('Storing credentials to ' + credential_path) return credentials
リソースにアクセスするクライアントアプリ
リソース(Google Drive)のクレデンシャル(アクセストークン)を取得し、リソース(Google Drive)のファイルリストを表示するPythonソースコードを示します。
def main(): """Shows basic usage of the Google Drive API. Creates a Google Drive API service object and outputs the names and IDs for up to 10 files. """ credentials = get_credentials() http = credentials.authorize(httplib2.Http()) service = discovery.build('drive', 'v3', http=http) results = service.files().list( pageSize=10,fields="nextPageToken, files(id, name)").execute() items = results.get('files', []) if not items: print('No files found.') else: print('Files:') for item in items: print('{0} ({1})'.format(item['name'], item['id'])) if __name__ == '__main__': main()
Google Drive APIのOAuth 2.0 SCOPE情報
上記アプリでは、SCOPESに「https://www.googleapis.com/auth/drive.readonly」を設定していますが、Google Drive APIのOAuth 2.0 SCOPE情報には以下が指定できます。アプリの機能に対する適切なSCOPE情報を設定してください。
SCOPE | 意味 |
https://www.googleapis.com/auth/drive | Application Dataフォルダを除く、ユーザーの全てのファイルへ完全なアクセスを許可します。このスコープは、厳密に必要な場合にのみリクエストしてください。 |
https://www.googleapis.com/auth/drive.readonly | ファイルメタデータとファイルコンテンツへの読み取り専用アクセスを許可します。 |
https://www.googleapis.com/auth/drive.appfolder | Application Dataフォルダへのアクセスを許可します。 |
https://www.googleapis.com/auth/drive.file | アプリで作成または開いたファイルへのファイル単位のアクセスを許可します。 |
https://www.googleapis.com/auth/drive.install | ユーザーがアプリのインストールを承認できるようにするための特別なスコープです。 |
https://www.googleapis.com/auth/drive.metadata | ファイルメタデータ(downloadUrlおよびcontentHints.thumbnailを除く)への読み書きアクセスを許可しますが、ファイルコンテンツの読み取り、ダウンロード、書き込み、アップロードへのアクセスは許可されません。ファイルの作成、削除、または削除をサポートしません。 また、アクセスのエスカレーションを防ぐためにフォルダや共有を変更することもできません。 |
https://www.googleapis.com/auth/drive.metadata.readonly | ファイルメタデータ(downloadUrlとcontentHints.thumbnailを除く)への読み取り専用アクセスを許可しますが、ファイルコンテンツの読み取りやダウンロードは許可しません。 |
https://www.googleapis.com/auth/drive.photos.readonly | すべての写真に読み取り専用アクセスを許可します。 spacesパラメータはphotosに設定する必要があります。 |
https://www.googleapis.com/auth/drive.scripts | Apps Scriptファイルへのアクセスを許可します。 |
クライアントアプリ実行時の認証プロセス
クライアントアプリの実行
クライアントアプリを実行する前に、以下の準備をしておきます。
- 認証情報ファイルを所定の場所(アプリのコードに合わせ、「client_secret.json」にリネームし、ここではアプリのソースコードと同じ場所)へ配備します。
- クレデンシャルファイル(アクセストークン)の格納ディレクトリ(ここでは「~/.credentials」)を作成しておきます。
上記で作成した、Google Driveのファイル一覧を表示するアプリを実行します。
$ python listfiles.py |
- Pythonコマンドでアプリ「listfiles.py」を実行します。
[注意]リモートでアプリを実行する場合は、リモートデスクトップ接続などでアプリを起動してください。Tera TermなどSSHでアプリを起動してもブラウザと連携した認証プロセスが実行できません。
リソースオーナへの認証リクエスト
ブラウザでGoogleへログインしている場合はアカウントの選択画面が表示されます。ログインしていない場合は、Googleアカウント(メールアドレス)とパスワード入力画面が表示されます。
- 「<プロジェクト名>に移動」をクリックすると、プロバイダの情報(メールアドレス、サービス名)が確認できます。
- 認証を与えるGoogle Driveのアカウントを選択します。
- クライアントアプリからのリクエストに応じる場合は「許可」をクリックします。
認証に成功すると以下の画面が表示されます。
アクセストークンによるリソースへのアクセス
認証が成功するとリソース(Google Drive)へアクセスするためのアクセストークン(ここでは「google-drive-credentials.json」ファイル)が生成されます。以降のアプリ実行ではこのファイルを使用しリソース(Google Drive)へアクセスするので、毎回認証を求められることはありません。
[参考]Google Drive APIs > REST > Python Quickstart