ラベル patreon の投稿を表示しています。 すべての投稿を表示
ラベル patreon の投稿を表示しています。 すべての投稿を表示

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 で管理したほうがいいかなという感じです

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

参考サイト

2018年10月14日日曜日

Patreon の OAuth 認証を使ってみた

概要

Patreon には OAuth の仕組みがありこれを使えば Patreon のサーバ情報に API を使ってアクセスすることができます
今回は OAuth を実現するためのログイン画面の使い方から OAuth 後の API の呼び出しまで基本的な流れを試してみました

環境

  • macOS 10.14
  • Ruby 2.5.1p57
  • patreon-ruby 0.5.0

クライアントアプリ作成

まずは OAuth 用のクライアントアプリを作成しましょう
このページから作成できます
コールバック用の URL は localhost で動作させるアプリを指定します

patreon_oauth3.png

クライアントを作成すると「Client ID」と「Client Secret」が取得できるのでメモしておきましょう

ライブラリインストール

  • bundle init
  • vim Gemfile
gem "sinatra"
gem "patreon"
  • bundle install --path vendor

アプリ作成

今回のアプリの流れとしては

  • Patreon でログインページへ遷移
  • ログインできた場合はコールバック用のページでトークンを取得
  • 取得したトークンを使って Patreon の情報を取得

になります

ログイン画面の作成

まずは Patreon のログイン画面に遷移させるページを作成します

  • vim app.rb
require 'sinatra/base'

class TestOAuth < Sinatra::Base
  get '/login' do
    erb :login
  end
end

/login にアクセスした場合にログイン画面に遷移するリンクを表示します

  • mkdir views
  • vim views/login.erb
<html>
<head>
</head>
<body>
  <a href="https://www.patreon.com/oauth2/authorize?response_type=code&client_id=0xmmvlIcKC0PAhdZHdQ31myNO1qPD4MDQBqHOLoZQ19n5DCmfupfyZdlhwv8ikMe&redirect_uri=http://localhost:9292/callback">login</a>
</body>
</html>

Patreon のログイン画面へのリンクにはフォーマットが決められており https://www.patreon.com/oauth2/authorize に対して response_typeclient_idredirect_url をパラメータに付与してリクエストします
ここで client_id は先程クライアント作成時にメモしておいた「Client ID」を記載してください
また redirect_url もクライアントアプリを作成するときに指定した URL を指定してください
間違っている場合ログイン画面が表示されません

コールバック用のページの作成

ログインに成功した場合に呼び出されるコールバック用のページを作成します
app.rb にコールバック用のリクエストを受け付けるルーティング /callback を追加します

  • vim app.rb
require 'sinatra/base'
require 'patreon'

class TestOAuth < Sinatra::Base
  get '/login' do
    erb :login
  end

  get '/callback' do
    client_id = '0xmmvlIcKC0PAhdZHdQ31myNO1qPD4MDQBqHOLoZQ19n5DCmfupfyZdlhwv8ikMe'
    client_secret = 'Xex5ENhpLeZwv7UdZWBF2HS6bLqwE6cUYQJVkQWevcBMBc2bINhKKeh-l069Uypq'
    redirect_url = 'http://localhost:9292/callback'

    oauth_client = Patreon::OAuth.new(client_id, client_secret)
    tokens = oauth_client.get_tokens(params['code'], redirect_url)

    api_client = Patreon::API.new(tokens['access_token'])
    user = api_client.fetch_user()
    @user_data = user.data
    erb :callback
  end
end

client_id, client_secret は作成したクライアントのものを指定してください
その 2 つからトークンを取得するための oauth_client を作成します
コールバックされたページには code というパラメータが付与されて呼び出されます
その code と redirect_url そして oauth_client を使って get_tokens メソッドを呼び出すことでトークン情報を取得することができます

トークンにはいくつか種類がありますが API をコールするために必要なのは access_token になります
ハッシュとして受け取れるので access_token にアクセスしましょう

あとはトークンを元に Patreon::API で API をコールするためのクライアントを作成し fetch_user などのメソッドをコールすれば OK です

今回は取得したデータをテンプレートに渡してそちらでアイコンと名前を表示します

  • vim views/callback.erb
<html>
<head>
</head>
<body>
  <h1><%= @user_data.full_name %></h1>
  <img src="<%= @user_data.thumb_url %>">
</body>
</html>

動作確認

  • bundle exec rackup config.ru

でアプリを起動しましょう
あとは localhost:9292/login にアクセスするとログインへのリンクが表示されるのでそれを踏み Patreon のログイン画面でログインすればユーザ情報が表示されるはずです

patreon_oauth_demo.gif

最後に

Patreon の OAuth 機能を使ってログインから情報を取得するまでの基本的な流れを紹介しました
OAuth 自体に特に難しかった点はなかったのですが、Ruby のクライアントライブラリの使い方などは知っておく必要がありそうです
公式に Sinatra の OAuth のサンプルもあったのでそれも参考にすると良いかもしれません

今回使用したクライアントアプリはすでに削除しているので client_idclient_secret は使えませんのでご注意ください

参考サイト

2018年8月20日月曜日

Patreon REST API を Ruby から使ってみた

概要

タイトルのとおりです
UI を構築するわけではないので OAuth を使ったログイン画面などは作成しません
今回は自身のアカウントの情報を取得してみたいと思います

環境

  • macOS 10.13.5
  • patreon-ruby 0.5.0
  • Ruby 2.5.1p57

クライアントアプリの登録

https://www.patreon.com/portal/registration/register-clients から登録しましょう
patreon_api1.png

アプリの必要な情報を入力します
patreon_api2.png

アイコンの URL プライバシーポリシーの URL、規約の URL は必須ではありません
以下のように作成できれば OK です
patreon_api3.png

今回は「Creator's Access Token」を使います

Patrons を取得するサンプルコード

Patrons は要するに支援してくれている方々です

require 'patreon'
require 'cgi'

access_token = 'creators_access_token'
api_client = Patreon::API.new(access_token)

campaign_response = api_client.fetch_campaign()
campaign_id = campaign_response.data[0].id

all_pledges = []
cursor = nil
while true do
  page_response = api_client.fetch_page_of_pledges(campaign_id, { :count => 25, :cursor => cursor })
  all_pledges.concat page_response.data
  next_page_link = page_response.links[page_response.data]['next']
  if next_page_link
    parsed_query = CGI::parse(next_page_link)
    cursor = parsed_query['page[cursor]'][0]
  else
    break
  end
end

puts all_pledges.map{ |pledge| { full_name: pledge.patron.full_name, amount_cents: pledge.amount_cents } }

Creator's Access Token を使って API クライアントを作成します
まず fetch_campaign()campaign_id を取得します
campaign_id はいわゆるユーザ ID みたいなものです

次にその ID を元に fetch_page_of_pledges() をコールします
ページネートしながら Patrons を取得することができます
次のページは page_response.links[page_response.data]['next'] で取得しています
これが nil であれば終了してそうでなければ次のページの URL をパースして cursol 情報を取得します

あとは最後に保存した配列の情報 (all_pledges) を map で必要なフィールドだけ取得して表示しています
map しているだけなのでそのまま puts しても OK です

おまけ

ちなみに今回の API であれば curl で簡単に呼べます

curl -XGET \
  -H 'Authorization: Bearer [creators_access_token]' \
  'https://www.patreon.com/api/oauth2/api/campaigns/[campaign_id]/pledges'

最後に

Patreon の REST API を Ruby でコールしてみました
今回はアプリを作成したユーザ自身の情報をしか取得しなかったのでログイン画面を作成しませんでした
もしログインしたユーザの情報を取得したい場合はログイン画面を作成して OAuth 認証してトークンをちゃんと取得して API をコールしてください

参考サイト