雑談
Oauth関係難しすぎて、2ヶ月くらいずっこけてました。
(たまにしか触らなかったせいかも...)
ようやく解決しそうです。備忘録行ってみよー
前提知識説明
googleauthって何?
googleauth
はGoogleのAPIにアクセスするための認証ライブラリです。
通常、Oauth2.0のトークンを一時的にメモリやファイルに保存しますが、データベースに保存することで、永続的に管理できるようになります。
TokenStoreって何?
Googleのgoogleauth
ライブラリでは、Google::Auth::TokenStore
というインターフェースを使って、OAuth 2.0 のトークンを保存・取得・削除できます。
ActiveRecord を使うと、データベースを TokenStore のストレージとして利用できるようになるはず。
Tokenって何?
Tokenとは、特定のユーザーやアプリケーションが認証されたことを証明するための文字列で、おそらくセキュリティ用途。
例えば、webアプリやモバイルアプリでユーザーがログインするとTokenが生成され、サーバとのやり取りでトークンを使って認証を行います。
Google認証あたりを作っていて登場した、アクセストークン と リフレッシュトークンについてさらに説明します。
アクセストークンって何?
役割:ユーザーが認証されていることを証明し、APIやサービスにアクセスするためのキーとなる存在です。このトークンを使用することでサーバと通信し、データなどにアクセスすることが可能になります。
特徴:有効期限が短命。期限が切れると使用できません。
リフレッシュトークンって何?
役割:アクセストークンが期限切れになった後、新しいアクセストークンを取得するために使用されます。リフレッシュトークンを使用して、再度ユーザーの認証を行うことなく、アクセストークンを再発行することが可能です。
特徴:有効期限が長命。
tokenを永続的に使うことできるの?
結論:アクセストークンは一定時間で失効するが、リフレッシュトークンを保存すれば永続的に利用できる。
- アクセストークンには有効期限がある。(Googleだと1時間)
- 期限が切れたら、新しいアクセストークンを取得する必要がある
- そのため、リフレッシュトークンを保存し、アクセストークンが切れたら自動更新しなければならない
実装編
Google API を Ruby からアクセスするための gem として googleauth というのがあります。これは、 OAuth2 のトークンをファイルや DB に保存しておくように作られていますが、中に含まれているトークン保存のためのクラス (TokenStore) は以下の2種類しかありません。
やりたいことと少し違ったので、作ろうという話です。
まず、Tokenを保存したいので DBにTokenテーブル的なものを作成しましょう。
$ rails g model google_auth_token token_key:string client_id:string access_token:string refresh_token:string expiration_time_millis:bigint user_id:integer
$ rails db:migrate
データを保存する場所ができたので、次にapp/models/active_record_token_store.rb
以下の文を記入してください。
class ActiveRecordTokenStore < Google::Auth::TokenStore
# Create a new store with the supplied file.
#
# @param [String, File] file
# Path to storage file
def initialize(user_id)
puts "確認0"
@user_id = user_id
end
# (see Google::Auth::Stores::TokenStore#load)
def load(id)
puts "確認1"
record = GoogleAuthToken.find_by! user_id: @user_id, token_key: id
record.to_json
end
# (see Google::Auth::Stores::TokenStore#store)
def store(id, token)
puts "確認2"
json = JSON.parse(token)
client_id = json["client_id"]
access_token = json["access_token"]
refresh_token = json["refresh_token"]
expiration_time_millis = json["expiration_time_millis"]
record = GoogleAuthToken.find_or_initialize_by user_id: @user_id, token_key: id, client_id: client_id, access_token: access_token, refresh_token: refresh_token, expiration_time_millis: expiration_time_millis
record.save
end
# (see Google::Auth::Stores::TokenStore#delete)
def delete(id)
record = GoogleAuthToken.find_by! user_id: @user_id, token_key: id
record.destroy
end
end
使い方
使いたいところで、ActiveRecordTokenStore.new(user_id)
を書いてください。
例
oauth_client = Google::Auth::UserAuthorizer.new(
Google::Auth::ClientId.from_file(token_path),
scope,
ActiveRecordTokenStore.new(0),
callback_path
)
おまけ
私が上のコードをやっても、何故かめっちゃエラーで転けてました。↓
Signet::AuthorizationError: Authorization failed. Server message: (Signet::AuthorizationError)
{
"error": "unsupported_grant_type",
"error_description": "Invalid grant_type: "
}
DBにもtokenなど何も保存されず、空文字が入っていました。
自分の場合は、至る所にputs
でどこまで処理できてるか確認しました。
そして結局は、route.rbが悪かったです。ssession#callbackの中の処理が実行されてませんでした。
ポカミスには気をつけましょう〜