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?

RailsでGoogleAuthのカスタムTokenStoreを作る話

Posted at

雑談

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の中の処理が実行されてませんでした。

ポカミスには気をつけましょう〜

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?