0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

gmailのAPIを触りながらOAuthを理解する

Posted at

OAuth2とは

あるリソースに対する認可を
ユーザーの認可操作を通して
第三者のアプリにケーションに付与する方法

補足:

  • Basic認証よりもセキュア
    • パスワードを直接渡さない
    • トークンを無効化することでアクセスを取り消せる
  • 認可の権限を細かく制御できる

認証フローの実例

前提条件

  • GCPでgmailのapiの設定とOAuthクライアントの設定は済んでいる
  • クライアントの認証情報を示すcredentials.jsonはすでに配置されている

初回接続時

1. credentials.json を読み込み
2. ブラウザでGoogleログインページを開く
3. ユーザーが権限を承認
4. Access Token + Refresh Token を取得
5. token.json に保存

2回目以降

1. token.json から認証情報を読み込み
2. Access Token が有効検証
3. 有効でない場合、Refresh Token で Access Token を更新
4. token.json を更新(これをしないとAccess Tokenが更新されず、apiを呼び出すたびにrefreshの処理が入ることになる)

token.jsonの形式

{
    "token": "xxxxx",
    "refresh_token": "yyyyy",
    "token_uri": "https://oauth2.googleapis.com/token",
    "client_id": "your_client_id",
    "client_secret": "your_client_secret",
    "scopes": ["https://www.googleapis.com/auth/gmail.readonly"],
    "universe_domain": "googleapis.com",
    "account": "",
    "expiry": "2025-10-20T09:18:16Z"
}

認証操作コード例

# import
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build

# helper functions
def save_token(creds):
    """Save the credentials to token.json."""
    with open("token.json", "w") as token:
        token.write(creds.to_json())


def initialize_token():
    """Initialize token.json by running the OAuth2 flow.
    Save the obtained credentials to token.json in local.
    Returns the obtained Credentials object.
    """
    flow = InstalledAppFlow.from_client_secrets_file(
        "credentials.json",
        SCOPES
    )
    creds = flow.run_local_server(port=0)
    save_token(creds)

    return creds


# main functions
def build_creds():
    """ Build credentials for GCP API access."""
    if os.path.exists("token.json"):
        creds = Credentials.from_authorized_user_file("token.json", SCOPES)
    else:
        creds = initialize_token()

    if not creds.valid:
        if creds.expired and creds.refresh_token:
            creds.refresh(Request())
            save_token(creds)
        else:
            creds = initialize_token()

    return creds


def build_gmail_service():
    """ Build service for GCP API access."""
    creds = build_creds()
    service = build("gmail", "v1", credentials=creds)
    return service

認証コード使用例

直近5件のthreadを取得するコード

service = build_gmail_service()
results = service.users().threads().list(
	userId='me',
	maxResults=5
).execute()
results
---
{
	'threads': [
		{
			'id': 'aaaa',
			'snippet': 'bbb',
			'historyId': 'ccc'
		}, ...
	],
	'nextPageToken': '16135562161548514955',
	'resultSizeEstimate': 201
}

トラブルシューティング

※AIによる提案

  • Access Token が期限切れになった
    → 自動的に Refresh Token で更新される。

  • Refresh Token も無効になった
    → 再度 OAuth フローを実行する必要があります。token.json を削除して再実行。

  • 権限エラーが出る
    SCOPES の設定を確認。変更した場合は token.json を削除して再認証。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?