JavascriptでGoogle Calendar API連携を実装する際、gapi.auth2.getAuthInstance()でAPI連携認証を行うと、アクセストークンの有効期限が1時間であることから、アクセスのたびに連携認証画面へ遷移する必要があります。
ユーザビリティ向上のため、API連携認証は初回アクセス時のみ行い、サーバ側でリフレッシュトークンを管理し、ユーザのアクセスがあるごとにアクセストークンを発行するという仕様にします。
今回の内容
- refresh_token, access_tokenの取得
access_tokenを用いてAPIにアクセスする方法は下記を参照
OAuth2.0でrefresh_tokenを取得してGoogle APIにアクセス(access_tokenでAPIにアクセス)
全体の流れ
始める前に
Google APIを使用するにあたり、クライアントIDとクライアントシークレットが必要になります。取得方法についてはこちらを参考にしました。
以下の認証を行うにあたり、gapi.auth2.getAuthInstance()などでアプリ連携が認証済みだとrefresh_tokenが発行されないため、アプリ連携を解除してから行ってください。アプリ連携解除はこちらから行います。https://myaccount.google.com/permissions
クライアント側でのAPI連携認証
// 有効なリフレッシュトークンが存在しない場合に実行
let params = {
response_type: 'code',
client_id: process.env.REACT_APP_CLIENT_ID,
redirect_uri: `${location.protocol}//${location.host}/refresh_token/`, // サーバサイドで/refresh_token/にアクセスできるようにしておく
approval_prompt: 'force',
scope: 'https://www.googleapis.com/auth/calendar',
access_type: 'offline'
}
let url = 'https://accounts.google.com/o/oauth2/v2/auth'
Object.keys(params).map((key, i)=>{
url += `${i===0 ? '?' : '&'}${key}=${params[key]}`
})
location.href = url
scopeは使用するAPIの種類に応じて適宜変更してください。
location.href = urlによってブラウザ上で認証画面に切り替わります。
params内のredirect_uriはユーザによる認証が行われた後にブラウザ上でリダイレクトされるURLです。
このURLは事前にGoogle API Console上でリダイレクトURLとして登録しておく必要があります。
リダイレクト時は以下のようにURLパラメータが付くので,サーバサイドでこれを受け取れるようにしておきます。
https://{host}/refresh_token/?code=XXXXXXXXXXXXXXXXXXXX
サーバサイドでの処理(refresh_tokenの取得)
サーバサイドはDjangoを想定しています。
// サーバサイドで認証コードを受け取り、リフレッシュトークンを発行
import rerquests
@login_required
def refresh_token(request):
code = request.GET.get('code', None) # URLパラメータからcodeを抽出
if code is not None:
# POST
data = {
'code': code,
'client_id': 'client_id_xxxxxxxxxxxxxxxxxxxx',
'client_secret': 'client_secret_xxxxxxxxxxxxxxxxxxxx',
'redirect_uri': 'https://{host}/refresh_token',
'grant_type': 'authorization_code',
'access_type': 'offline',
}
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
url = 'https://www.googleapis.com/oauth2/v4/token'
res = requests.post(url, data=data, headers=headers)
# refresh_tokenを取得し、データベースに保存する
refresh_token = res.json().get('refresh_token', None)
if refresh_token is not None:
user = request.user
user.refresh_token = refresh_token
user.save()
return redirect('index:index') # アプリページへリダイレクト
上記のPOSTでcodeが有効であればrefresh_tokenが取得できるはずです。
サーバサイドでの処理(access_tokenの取得)
refresh_tokenを使用してaccess_tokenを取得します
def get_access_token(request):
# POST
data = {
'refresh_token': user.refresh_token, # userに保存したrefresh_tokenを使用
'client_id': 'client_id_xxxxxxxxxxxxxxxxxxxx',
'client_secret': 'client_secret_xxxxxxxxxxxxxxxxxxxx',
'redirect_uri': 'https://{host}/refresh_token',
'grant_type': 'refresh_token',
'access_type': 'offline',
}
url = 'https://www.googleapis.com/oauth2/v4/token'
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
res = requests.post(url, data=data, headers=headers)
# access_tokenを取得し、データベースに保存する
access_token = res.json().get('access_token', None)
if access_token is not None:
user = request.user
user.access_token = access_token
user.save()
return access_token
return ''
access_tokenの検証
取得したaccess_tokenが有効か検証することもできます
def validate_access_token(access_token):
url = 'https://www.googleapis.com/oauth2/v3/tokeninfo'
res = requests.get(url, {'access_token': access_token})
if res.json().get('error_description', None) is None:
return True # valid
return False # invalid
次のようなJSONが返されます
// access_tokenが有効な時
{
"azp": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com",
"aud": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com",
"scope": "https://www.googleapis.com/auth/calendar",
"exp": "1234567890",
"expires_in": "3554", // 残り有効時間(秒)
"access_type": "offline"
}
// access_tokenが無効な時
{
"error_description": "Invalid Value"
}
取得したaccess_tokenを使用してAPIにアクセスする方法は次のページで説明します。
OAuth2.0でrefresh_tokenを取得してGoogle APIにアクセス(access_tokenでAPIにアクセス)
コメント