無粋な日々に

頭の中のメモ。分からないことを整理する

APIキー、OAuthクライアントID、サービスアカウントキーの違い:Google APIs

GoogleのサービスをAPI経由で利用する際、大きく3つの認証情報が登場します。「APIキー」、「OAuth2.0クライアントID」、「サービスアカウントキー」です。本投稿ではこれらの用途をざっくりと整理し、取得方法、Pythonでの使い方の違いを説明します。

f:id:messefor:20201006223438p:plain

まとめ

  • Google APIsを使うには、下表の3ついずれかの認証情報が必要です
  • APIどういうデータに、どういう形でアクセスするかに依って使うべき認証情報が異なります
  • いずれの認証情報もGoogle Cloud ConsoleAPIとサービス > 認証情報から作成することができます
    • Googleアカウントが無い方は、まずGoogleアカウントを作成し、Google Cloud Consoleでプロジェクト作成が必要です
# 必要となるもの アクセス可能なデータ 誰としてアクセスするか 具体例
1 APIキー 一般公開データ 匿名ユーザ YouTubeにある動画をアプリケーション経由で検索する
2 OAuth2.0クライアトID 一般公開データ/ユーザーデータ ユーザアカウント あるユーザ(エンドユーザ)の代わりにユーザのGoogleドライブにアプリケーションを経由でデータを保存
3 サービスアカウントキー 一般公開データ/ユーザーデータ サービスアカウント 共同作業メンバのGoogleカレンダー情報にアプリケーションを経由してアクセスする

基本事項のおさらい

認証情報について理解する上で知っておいたほうが良い基本事項についてまとめます。必要ない方は飛ばしてください。

APIの性質

APIApplication Programming Interface)はサービスを外部のプログラムやアプリケーションから利用できるようにした窓口です。例えば、Google MapはGoogleのサービスの一つで、普通私たちユーザはブラウザやGoogle Map Appを自分で操作してGoogle Mapを利用します。一方で自身が開発するアプリの中でGoogle Map機能の一部を利用したい場合は、アプリの中のプログラムを介してGoogle Map機能を操作する必要があります。ここで利用されるのがAPIというプログラムから呼び出すインターフェースです。

一般にAPIはプログラムやアプリケーションから利用されます

認証情報の役割

Google は許可されたアプリケーションからしAPI利用をさせてくれません。そのため我々API利用者はGoogleアカウントを作成し、Google Cloud Console内でAPIを利用するアプリケーションの登録を行う必要があります。

つまりどの開発者が登録した、どのアプリケーションがAPIを叩いているのかをGoolge様にお知らせするのがAPIキーやOAuth2.0クライアトIDなど認証情報の役割の一つです。

またAPI利用は使用量の無料枠上限を超えると課金が発生します。API利用がどのアプリでどの程度発生しているかをGoogle様にお知らせするのも認証情報の役割です。課金管理はプロジェクト単位で行われるため認証情報はGoogleアカウント内のプロジェクトと紐付ける必要があります。これが認証情報を作成する際にプロジェクトが必要な理由です。

3つの認証情報は想定される利用シーンが異なる

APIキー、OAuth2.0クライアントID、サービスアカウントキーの3種類のいずれかがあればAPIを使うことが可能です。ただし三者は用途が異なり、用途にあった認証情報を選択するのが自然です。たとえば一般公開データにしかアクセスしないのに、OAuthクライアントIDを使う必要はありません。用途に応じた必要最低限の認証情報を使います。

以上を踏まえて3つの認証情報をそれぞれ見ていきましょう。

1. APIキー

まずはAPIキーについてです。Google Cloud ドキュメント:API キーの使用には次のように説明されています。

API キーは、プリンシパルなしでアプリケーションを識別する暗号化された単純な文字列です。一般公開データに匿名でアクセスする場合に便利で、割り当てや課金のために API リクエストをプロジェクトに関連付けるために使用されます。

YouTubeでの検索機能のように一般公開データにのみアクセスできれば十分な場合、利用者にユーザアカウントでログインしてもらうような必要はありません。このようなケースではAPIキーを使ってAPIを利用します。APIキーは暗号化された単純な文字列で、作成も取り扱いも比較的簡単です。

取得方法

事前にGoogle Cloud アカウント作成とプロジェクト作成が必要です。まだな方は以下のサイトなどを参考にして、アカウントとプロジェクト作成を行ってください。

Google アカウントを作成する(Gmail アドレスも同時に作成) | MAGELLAN BLOCKS

Google アカウントを作成する(Gmail アドレスも同時に作成) | MAGELLAN BLOCKS

プロジェクト作成が完了したら次の手順でAPIキーを作成します。

1. プロジェクトを選択

Google Cloud Consoleにログインして、APIを利用するプロジェクトを選択します。

f:id:messefor:20201005221836p:plain:w250

APIとサービス > 認証情報

f:id:messefor:20201005221902p:plain:w250

f:id:messefor:20201005221928p:plain:w250

2. APIキーの作成

「認証情報の作成」をクリック

f:id:messefor:20201005221959p:plain:w250

APIキー」を選択

f:id:messefor:20201005222022p:plain:w250

APIキーが表示されたボックスの右側にあるコピーマークをクリックし、閉じる

f:id:messefor:20201005222040p:plain:w250

3. キーで利用するAPIを制限する(オプショナル)

作成直後の状態ではすべてのAPIの利用が可能となっているので、利用したいAPIのみに制限します

作成したAPIキーを選択

f:id:messefor:20201005222059p:plain:w250

APIの制限 > キーを制限

f:id:messefor:20201005222115p:plain:w250

Pythonでの利用例

例として、PythonからAPIキーを使ってYouTubeのsearchAPIを使うサンプルコードを掲載します。 下記コードのYOUTUBE_API_SERVICE_NAMEYOUTUBE_API_VERSIONを変えるといろいろなAPIを使えるようになります。

from googleapiclient.discovery import build

# 利用するAPIサービス
YOUTUBE_API_SERVICE_NAME = 'youtube'
YOUTUBE_API_VERSION = 'v3'

# APIキーを指定
YOUTUBE_API_KEY = 'Your-API-KEY-here'

# API のビルドと初期化
youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION,
            developerKey=YOUTUBE_API_KEY)

# YouTubeでキーワード検索(視聴数の多い順)
q = 'Overjoyed'
response = youtube.search().list(part='snippet',
                                q=q,
                                order='viewCount',
                                type='video',).execute()

response

>>>  # 出力

{'etag': 'QvSya4g44W8aUkApKibFr-o5JqA',
 'items': [{'etag': 'Jw9SPInmYcoJg21LlKvbdDhk1IU',
            'id': {'kind': 'youtube#video', 'videoId': '_a1LogyX9Uw'},
            'kind': 'youtube#searchResult',
            'snippet': {'channelId': 'UCOo4Oc-PYSrzEeappVyoM-Q',
                        'channelTitle': 'beechoppers',
                        'description': 'please enjoy the music! music and '
                                       'lyrics by stevie wonder clip design by '
                                       'R. Norton.',
                        'liveBroadcastContent': 'none',
...

2.OAuth2.0クライントID

アプリ経由で利用者のGoogleドライブにファイルを保存をする場合など、アプリケーションがエンドユーザーに代わって Google Cloud APIs にアクセスします。このケースで利用するのがOAuth2.0クライアントIDです。OAuth2.0プロトコルを利用することで、アプリケーションが特定ユーザとしてサービスを利用します。その際に許可する権限の範囲(スコープ)も制限します。

f:id:messefor:20201005230723p:plain

OAuth2.0クライントIDは、OAuth2.0クライアントクレデンシャル(資格情報)というJSONファイルとしてダウンロードし利用することが多いと思います。OAuth2.0クライアントクレデンシャルには、OAuth2.0認証を利用するアプリの情報(クライアントID、クライアントシークレット)が含まれます。クレデンシャルはGoogle様にアプリ開発者がOAuth認証を行う資格があることを示します。

取得方法

OAuth認証は作成する同意画面の設定やアプリの種類など状況によって適切に設定する必要がありますが、本投稿ではテストとして最短で利用する手順を書いてみます。

1.同意画面の設定

OAuth認証では、認証時ユーザにログインをしてもらったり、許可してもらう権限の範囲を示す必要があるため、認証画面に表示する情報は重要です。以下はconnpassTwitterアカウントでログインする際のOAuth認証画面です。画面にはconnpassのロゴなど表示され、どのアプリがアカウント情報を利用しようとしているかひと目で分かります。

f:id:messefor:20201005230918p:plain:w400

このユーザに同意を求める部分の設定を「同意画面の設定」から行っていきます。今回はテストなのでなるべく手軽に行うことを目指します。

APIキー同様、まずGoogle Cloud Consoleにログインして、APIを利用するプロジェクトを選択します。

f:id:messefor:20201005224733p:plain:w250

APIとサービス > OAuth同意画面を選択し、適当なアプリ名とメールアドレスを入力します

f:id:messefor:20201005225025p:plain:w250

UserTypeを聞かれるので、今回は「外部」を選びます。「内部」は使ったことありませんが、G Suiteの組織内で利用するときに使うようです。

f:id:messefor:20201005225113p:plain:w250

省略できる箇所は省略し、試すのに必要最低限な入力をして「保存して次へ」で進めます。入力が必要なのはメールアドレスのみです。実際にアプリを開発する際はスコープなど慎重に設定しましょう。

f:id:messefor:20201005225301p:plain:w250

f:id:messefor:20201005225358p:plain:w250

f:id:messefor:20201005225404p:plain:w250

f:id:messefor:20201005225409p:plain:w250

2.OAuthクライアントIDの作成

「+認証情報を作成」から「OAuthクライアントID」を選択します f:id:messefor:20201005225647p:plain:w250

アプリケーションの種類はここでは、デスクトップを選択しています f:id:messefor:20201005230153p:plain:w250

これでOAuthクライアントクレデンシャルが作成されました f:id:messefor:20201005225857p:plain:w250

「OK」を押して、作成されたIDの右にある↓矢印からJSONをダウンロードします。

f:id:messefor:20201005230357p:plain:w250

これでOAuthクライアントIDを含む、OAuthクライアントクレデンシャルが発行されました。

Pythonでの利用例

例として、PythonからOAuthクレデンシャルを使ってYouTubeのplaylistAPIを使うサンプルコードを掲載します。以下はOAuth認証用の関数を定義をしています。この関数を使ってOAuth認証をし、その後ユーザデータにアクセスします。今回はYouTubeに自分用の再生リストを作成したいと思います。実行のコードを下部にあります。

'''OAuth認証用の関数'''

import os
import pickle

import numpy as np
import pandas as pd

from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request


def get_credentials(client_secret_file, scopes,
                    token_storage_pkl='token.pickle'):
    '''google_auth_oauthlibを利用してOAuth2認証

        下記URLのコードをほぼそのまま利用。Apache 2.0
        https://developers.google.com/drive/api/v3/quickstart/python#step_1_turn_on_the_api_name
    '''
    creds = None
    # token.pickleファイルにユーザのアクセス情報とトークンが保存される
    # ファイルは初回の認証フローで自動的に作成される
    if os.path.exists(token_storage_pkl):
        with open(token_storage_pkl, 'rb') as token:
            creds = pickle.load(token)

    # 有効なクレデンシャルがなければ、ユーザーにログインしてもらう
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                client_secret_file, scopes=scopes)
            creds = flow.run_local_server(port=0)

        # クレデンシャルを保存(次回以降の認証のため)
        with open(token_storage_pkl, 'wb') as token:
            pickle.dump(creds, token)

    return creds

以下のコードのCLIENT_SECRET_FILEに先程ダウンロードしたJSONを指定してください。これを実行するとまずOAuth認証のためにブラウザが立ち上がり利用者にGoogleアカウントでのログインを求めます。 認証が終わると「再生リストテスト」という名で空の再生リストが作成されます。

'''OAuth認証と再生リストの作成'''
# 利用するAPIサービス
YOUTUBE_API_SERVICE_NAME = 'youtube'
YOUTUBE_API_VERSION = 'v3'

# OAuthのスコープとクレデンシャルファイル
YOUTUBE_READ_WRITE_SCOPE = 'https://www.googleapis.com/auth/youtube'
CLIENT_SECRET_FILE = 'client_secret.json'

# OAuth認証:クレデンシャルを作成
creds = get_credentials(
                    client_secret_file=CLIENT_SECRET_FILE,
                    scopes=YOUTUBE_READ_WRITE_SCOPE
                    )

# API のビルドと初期化
youtube_auth = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION,
                    credentials=creds)

# Add Playlist
# This code creates a new, private playlist in the authorized user's channel.
playlists_insert_response = youtube_auth.playlists().insert(
  part="snippet,status",
  body=dict(
    snippet=dict(
      title='再生リストテスト',
      description='APIで作成したプレイリスト'
    ),
    status=dict(
      privacyStatus="private"
    )
  )
).execute()

3. サービスアカウントキー

サービスアカウントはアカウントの一種です。アカウントなのでメールアドレスが割り当てられますが、アプリケーションが利用するアカウントなのでユーザアカウントとは異なり、ログインなどはできません。利用者がこのアプリケーション用のアカウントとデータを共有することで、プログラムからユーザデータへのアクセスが可能になります。APIはこのサービスアカウントの代わってサービスの操作を行います。

用途としてはたとえば、自分のGoogleカレンダーをサービスアカウントと共有し、アプリケーションからAPI経由で閲覧や編集するなどがあります。

 サービスアカウントキーでの認証は、一般にサービスアカウントクレデンシャルというJSONファイルを使って行われます。サービスアカウントクレデンシャルにはアカウントのメールアドレスや、鍵情報、クライントIDなど認証に必要な情報が含まれています。これまでの認証と違ってアカウント認証もこの資格情報で行います。

取得方法

1. プロジェクトを選択

Google Cloud Consoleにログインして、APIを利用するプロジェクトを選択します。

f:id:messefor:20201005221836p:plain:w250

APIとサービス > 認証情報

f:id:messefor:20201005221902p:plain:w250

f:id:messefor:20201005221928p:plain:w250

2. サービスアカウントキーの作成

「+認証情報を作成」から「サービスアカウント」を選択 f:id:messefor:20201007213935p:plain:w250

サービスアカウント名を入力

f:id:messefor:20201007213940p:plain:w250

続行を選んで、完了します。

f:id:messefor:20201007213944p:plain:w250 f:id:messefor:20201007213950p:plain:w250

3. 鍵ペアの作成

作成したサービスアカウントを選んで f:id:messefor:20201007215021p:plain:w250

鍵を追加で、「新しい鍵を追加」してJSONを選びます。 ついでに、上部のメールアドレスもコピーしておきましょう。

f:id:messefor:20201007220531p:plain:w250

f:id:messefor:20201007214957p:plain:w250

保存した.jsonファイルを認証に利用します。

Pythonでの利用例

例として、Pythonからサービスアカウントキーを用いてGoogle スプレッドシートのセルの値を取得してみます。まずClould Consoleの各プロジェクトでGoogle Sheet APIを有効にしておきましょう。

APIとサービス > ライブラリ

f:id:messefor:20201007215809p:plain:w250

検索窓でsheetを入力して、Google Sheets APIを選ぶ

f:id:messefor:20201007215843p:plain:w250

Google Sheets APIを有効にします

f:id:messefor:20201007215444p:plain:w250

次の準備として、Google スプレッドシートで新規にスプレッドシートを作成し、作成したシートをサービスアカウントと共有(編集権限を与える)しておく必要があります。

何か入力して、右上の「share」を押下

f:id:messefor:20201007220147p:plain:w250

さきほどメモったサービスアカウントのメールアドレスを貼り付けて「OK」を押して、シート共有します。

f:id:messefor:20201007220754p:plain:w250

さらにシートのURLから以下のsheet_idをメモっておきましょう https://docs.google.com/spreadsheets/d/<sheet_id>

実行するコードこちらです。これでシートの値が取得できます。

from googleapiclient.discovery import build
from google.oauth2 import service_account

CREDENTIAL_FILE = 'credential_json_path'
credentials =\
  service_account.Credentials.from_service_account_file(CREDENTIAL_FILE)

SAMPLE_SPREADSHEET_ID = '1CL0pg_LDpASaHQqagDxBDKonmD64NRS6CQnpAbVDjhs'
SAMPLE_RANGE_NAME = 'Sheet1!A1:B4'

# Sheets APIビルド
service = build('sheets', 'v4', credentials=credentials)

# セルの値を取得
sheet = service.spreadsheets()
result = sheet.values().get(spreadsheetId=SAMPLE_SPREADSHEET_ID,
                            range=SAMPLE_RANGE_NAME).execute()
values = result.get('values')


>>>  # 出力

[['1', 'あ'], ['2', 'い'], ['3', 'う']]

APIで権限まわりは混乱しがちですね。ちょっと長くなりましたが整理してみました。日々精進