2019年3月1日金曜日

Patreon の access_token は 1 ヶ月で expire になるので注意が必要

概要

Patreon の API を使うには OAuth で取得したアクセストークンが必要になります
アクセストークンは 31 日 (2678400 秒) で expire になってしまうのでそのタイミングで再度認証が必要になります
Patreon にはアクセストークンをリフレッシュする仕組みがあり refresh_token を使うことでアクセストークンを更新することができます

今回はリフレッシュのやり方の紹介とアクセストークンの管理方法を考えたいと思います

環境

  • macOS 10.14.3
  • Ruby 2.5.1p57
    • patreon 0.5.0
  • Redis 4.0.9

リフレッシュするコード

アクセストークンをリフレッシュだけなら以下の通り簡単です

oauth_client = Patreon::OAuth.new(client_id, client_secret)
tokens = oauth_client.refresh_token(refresh_token, redirect_uri)
tokens['access_token']

これで OK です
client_id, client_secret は Patreon でクライアントアプリを登録した際に発行される定数です
変更されることはありません

問題になるのは refresh_token です
アクセストークンをリフレッシュすると、この refresh_token も更新されます
refresh_token も定数であれば何も考えず上記のコードを呼ぶだけなのですが refresh_toke も更新されるのでそうは行かなくなります

今回は Redis を使ってトークンの管理と更新の仕組みを考え見ます

Redis を使ったトークン管理のサンプルコード

準備とサンプルコードの全体は以下の通りです

  • bundle init
  • vim Gemfile
gem "patreon"
gem "redis"
  • bundle install --path vendor
  • vim app.rb
require 'patreon'
require 'redis'
require 'json'
require 'date'

client_id = 'xxxxxxx'
client_secret = 'xxxxxxx'
redirect_uri = 'http://localhost:9292/'

cli = Redis.new
tokens = JSON.parse(cli.get('tokens'))
token = tokens['access_token']
register = tokens['register_date']
if register.nil? || (Date.today - Date.parse(register)).to_i > 30
  refresh_token = tokens['refresh_token']
  oauth_client = Patreon::OAuth.new(client_id, client_secret)
  tokens = oauth_client.refresh_token(refresh_token, redirect_uri)
  tokens.store('register_date', Date.today)
  cli.set('tokens', tokens.to_json)
  token = tokens['access_token']
end
puts token

解説

大まかな流れとしては

  • Redis にトークンのキャッシュがあるかチェック
  • ない場合は新規でトークン取得
  • ある場合は register_date をチェック
  • すでに 30 日を越えている場合はトークンをリフレッシュ
  • キャッシュがあり期限を越えていない場合はキャッシュにあるトークンを取得

という流れになります
おそらくアクセストークンをクライアント側で管理する場合はだいたいこんな流れになると思います

Patreon から tokens を取得すると以下のような JSON が取得できます

{
    "access_token": "single use token",
    "refresh_token": "single use token",
    "expires_in": "token lifetime duration",
    "scope": "token scopes",
    "token_type": "Bearer"
}

expires_in にはトークンの期限が切れるまでの秒が格納されています
取得後すぐだと「2678400」が格納されています
残り時間なので随時減らさないとダメですがクライアント側ではそうも行かないので代わりに register_date を登録しています

register_date が 30 日以前であればリフレッシュトークン呼びます
またそもそも登録されていない場合もリフレッシュトークンを呼びます
今回の方法の事前に refresh_token を Redis に格納しておく必要があります
一度登録すれば FLUSHALL などをしない限り再度登録する必要はないのですが、この方法だと始めだけは必ず必要になります
クライアントの管理画面でリフレッシュトークンが取得できるのでそこで取得して以下のような感じでリフレッシュトークンを Redis に登録します 

  • set tokens '{"refresh_token":"xxxxxxx"}'

これであとは放置しておけばトークンの更新がスクリプトを呼ばれるたびに自動で行われるので管理の必要がなくなります

改善点

やはり refresh_token を最初に Redis に格納するのが面倒だなと思います
単純にコード内で変数として持っておいてもいいですが、アプリが再起動すると消えるのでキャッシュ側をあくまでも優先するようにしましょう

素直に新規でトークンを取得すればいいじゃないかと思うかもしれません
新規で取得する場合は get_tokens という関数を呼び出すのですが、この場合は当然 OAuth 認証になるのでブラウザを立ち上げ認証ページで認証しなければなりません
またリダイレクト URL も必要になるので Web アプリを用意しなければいけなくなります
なので refresh_token 関数を呼ぶことでトークンの取得と再取得も行っています
refresh_token はブラウザを開いて認証するがありません

最後に

Patreon のアクセストークンをクライアント側で管理する方法を考えてみました
Redis を使うので、それだけで面倒が増えるので微妙な気がしますが一番てっとり早いかなと思います
例えばローカルのファイルなどでもいいのですが、そうなるとアプリがスケールすることができません

アクセストークンが expire したら手動で更新する方法でもいいかと思いますが、それも面倒なので Redis で管理したほうがいいかなという感じです

他にいい方法があれば教えていただきたいです

参考サイト

0 件のコメント:

コメントを投稿