Google Drive APIを Rubyのプログラムで実行するまでの記録④
このドキュメントをもとに進めていく。
ステップ 2: Google の OAuth 2.0 サーバーにリダイレクトする
このエラーは、Rails7から導入された、オープンリダイレクトという仕組みらしい。外部URLにリダイレクトしようとすると、UnsafeRedirectErrorが出るようになっている。
参考記事:Rails7 で入ったオープンリダイレクト対策
エラー文を読むと、pass allow_other_host: true to redirect anywayと書いてある。
Google翻訳にかけると、「とにかくリダイレクトするには、allow_other_host: true を渡します」。リダイレクトさせたい場合はこれを設定すればいいようだ。
redirect_to auth_uri, allow_other_host: true
はい、問題なくリダイレクトできました。よかった。
ステップ 3: Google がユーザーに同意を求める
ここではコードパラメータを取得できればいいので、ページは表示されなくてOK。
ステップ 4: OAuth 2.0 サーバー応答を処理する
ブラウザーのアクセスバーには以下の認証コードが返ってきていた。
この部分がわからない。
ステップ 5: リフレッシュ トークンとアクセス トークンの認証コードを交換する
おそらくauth_codeのところに先ほど取得した認証コードを当てはめればOKそう。
問題は、これをどこで実行するか?
auth_client.code = auth_code
auth_client.fetch_access_token!
ここまでの処理は、Railsアプリケーションのsubject_dataコントローラのindexアクションで行ってきた。
localhost:3000/subject_dataにGETリクエストを送ると実行されるようになっていた。
class SubjectDataController < ApplicationController
def index
require 'google/apis/drive_v3'
require 'google/api_client/client_secrets'
# client_secret.jsonファイルを読み取ってオブジェクトを作成
client_secrets = Google::APIClient::ClientSecrets.load
auth_client = client_secrets.to_authorization
auth_client.update!(
:scope => 'https://www.googleapis.com/auth/drive.metadata.readonly',
:redirect_uri => 'http://localhost',
:additional_parameters => {
"access_type" => "offline", # offline access
"include_granted_scopes" => "true" # incremental auth
}
)
auth_uri = auth_client.authorization_uri.to_s
redirect_to auth_uri, allow_other_host: true
end
end
だが、redirect_toで別のページに飛ばして、そっちでこの状態になっている。
ここで、認証コードを何らかの変数に格納しつつ、つぎの処理(リフレッシュトークンを渡してアクセストークンと交換する)が行われるようにしなきゃいけない。
遷移先を/oauth2callbackにするとどうなる?
:redirect_uri => 'http://localhost/oauth2callback'
redirec_uri_mismatchというエラーが出た。上記のコードがどこと不一致だと言われているんだろう?
ああ、そうか。client_secrets.jsonファイルで指定したリダイレクトURLと一致してないと言われているのか。こっちではhttp://localhostと指定していたから。
"redirect_uris":["http://localhost"]
ただclient_secrets.jsonファイルを変更しただけではダメで、Goolgle CloudのOAuth 2.0クライアント設定で、承認済みのリダイレクトURIも修正した。
その結果、エラーは解消された。
アクセスバーのURLは変わったけど、画面の表示は変わらない。
サンプルコードを見ると、「/oauth2callback」にGETリクエストを送ったときに処理が実行されている。
get '/oauth2callback' do
(省略)
auth_client.code = request['code']
auth_client.fetch_access_token!
(省略)
end
ああ、そうか。Railsアプリケーションは、route.rbでそれを設定するんだった。
「/oauth2callback」にGETリクエストを送ったときになんとかコントローラのなんとかアクションで実行される、と設定すればいいのか。
違う。GETリクエストを送る先は何も「/oauth2callback」にこだわらなくていいんだ。今回は「/subject_data」にしてるから、さっきのリダイレクトURLの指定も「/subject_data」にいいのか。
サンプルコードの中のrequest['code']という部分が何を参照しているのかわからなかった。
ChatGPTに聞いて、認証コードを参照しているとわかった。
これでようやくこのif文の意味がだいたいわかってきた。
if request['code'] == nil
auth_uri = auth_client.authorization_uri.to_s
redirect to(auth_uri)
else
auth_client.code = request['code']
auth_client.fetch_access_token!
auth_client.client_secret = nil
end
認証コードを持ってないときは認証コードを作ってから認証用URLに飛ばし、認証コードを持っていたら、認証コード(リフレッシュトークン)とアクセストークンを交換する、という感じだろう。
コードを書いて実行したがうまくいかない。
if文のどっちが実行されているか確認するため、デバッグツールを使って確認してみる。
if request['code'] == nil # 認証コードを持っていなかった場合
auth_uri = auth_client.authorization_uri.to_s
binding.pry
redirect_to auth_uri, allow_other_host: true
else # 認証コードを持っている場合
auth_client.code = request['code']
auth_client.fetch_access_token!
end
すると、ブラウザから「http://localhost:3000/subject_data」にアクセスしたらbinding.pryが実行された。
ということは、request['code']がnil、つまり「認証コードが空」の状態だ。(まだ認証画面に行ってないので当たり前)
警告によると、request['code']よりrequest.params['code']を使うのが好ましいらしい。
ここまで3時間。
あああ、もしかして、ポートの設定をしていないから???当たった!!
リダイレクトURLの設定を'http://localhost:3000/subject_data'に変更したら、認証コードを持った場合の処理が実行された。
binding.pryで確認して、赤枠の処理が実行されていることを確認した。
認証コードが入っていることを確認した。すごくうれしい。
auth_clientの中身を見てみると、いくつかわかったことがある。
この段階では、リフレッシュトークンは作られたが、アクセストークンはまだ作られていない。
@expires_atの値は、auth_clientオブジェクトの有効期限?
有効期限は1時間となっている。
ここまで3時間半。この30分の進捗は大きい。
APIを呼び出すにはアクセストークンが必要なので、アクセストークンがなかったらダメじゃん、と思って再度binding.pryで確認してみると、無事作られていた。
これで認証コードとアクセストークンの交換まで完了した。
ただ、ドキュメントを見るとあと3行ある。
アクセストークンを取得した後、クライアントシークレットをクリアしている。
auth_client.client_secret = nil
クライアントシークレットは、Google CloudでOAuth 2.0 クライアントを作った時に発行されたものだ。
これはセキュリティ上の対策として行うのが一般的なようだ。
アクセストークンを取得できた時点でもう使わないので、漏洩しないために破棄する。
つぎに、auth_clientオブジェクトをJSON形式に変換して、sessionハッシュにcrendentialsキーに対応する値として格納している。
session[:credentials] = auth_client.to_json
いったん上記2行を追加して再度実行するとエラーになった。
ビューがないと言われている。
そりゃそうだ。作ってないから。
エラーは出たが、アクセス許可通知のメールが届いたので、認証の処理はうまくいったのだと思う。
ここまでで4時間。ここで一旦中断。
長くなったので続きは別の記事で書くことにしよう。
この記事が気に入ったらサポートをしてみませんか?