ReactでSPAを作り、Twitter認証(OAuth)でログインする。バックエンドはRails

ReactでSPAを作り、Twitter認証(OAuth)でログインする。バックエンドはRails

ReactでSPAを作り、Twitter認証(OAuth)でログインする。バックエンドはRails

ReactでSPAを作り、Twitter認証(OAuth)でログインする。バックエンドはRails へのコメントはまだありません

はじめに

ReactでSPAを作ることになって、ログイン認証にメール&パスワードの認証に加えて、
GoogleやTwitter認証も加えたくなった時、どうすればよいかわからなかった。
バックエンドはRailsだが、あくまでAPIサーバー的な立場なので メジャーなomniauth
なんかは使えそうにない。。。
前回のGoogle編に続き、今回はTwitter編。
あれこれ悩みながらなんとか動くところまでたどり着いたので、やり方を公開。

React+Reduxの環境なんですが、そこまで立ち入ると話がややこしくなるので、
実装のエキス部分だけを記載してみます。

Twitter Developerにアプリ登録

Twitter Developer

これについては上記のURLにアクセスしてアプリを登録してください。
アプリ名とコールバックURLを登録したら、Consumer Key (API Key)とConsumer Secret (API Secret)がもらえます。
詳しくは書きません、ググればこれはたくさん書いてあります。

oauth-15

動作イメージ

デザインはしょぼいですが、こんな感じのログイン画面。
GoogleかTwitterで認証してログインしてね、というもの。
今回はTwitterについてのみ取り上げます。

Twitterでログインをクリックすると、Twitterが用意しているOAuth用の画面が表示されて、
Twitterのユーザー名とパスワードを入れて、最後に今回作るアプリ(SPA)へのアクセス許可画面が表示され、
許可すると、SPAアプリにログイン出来るというもの。

oauth-1

oauth-12

oauth-11

oauth-5

ステップ1(Javascript、Ajax)

まずは
まずはTwitterのOAuth用URLの取得のためのRails API(自作)呼び出し。
Reactの書き方なので、functionらしくないですが・・・ただのfunctionだと思ってソースを見てください。

API呼び出しの結果、OAuth用のURLが得られるので、window.openする感じです。
ポップアップというか別タブでTwitterのOAuth画面が開きます。
で、window.openしたあとは setTimeoutで1秒おきにウィンドウが閉じたかどうかをチェックします。

[bash]
onLoginTwitter: (history) => {
axios.get(“http://localhost:3000/get_twitter_oauth_url”, {
withCredentials: true,
params: {}
})
.then(function (response) {
windowLogin = window.open(response.data.oauth_url)
setTimeout(function(){CheckLoginStatusTwitter(history)}, 1000)
windowLogin.focus()
})
.catch(function (response) {
console.log(response);
});
}

[/bash]

ステップ1.5(Rails)

ステップ1から呼び出されるRailsのソース。
CONSUMER_IDとCONSUMER_SECRETを用いて、OAuth用のURLを取得し、JSONで結果を戻します。

[bash]
require ‘oauth’

def get_twitter_oauth_url
consumer = OAuth::Consumer.new(
Oauth::TWITTER_CONSUMER_ID,
Oauth::TWITTER_CONSUMER_SECRET,
{ :site => “https://api.twitter.com” }
)

request_token = consumer.get_request_token(
:oauth_callback => “http://localhost:3000/oauth_twitter”
)

cookies[:request_token] = request_token.token
cookies[:request_token_secret] = request_token.secret

rtn = {}
rtn[“status”] = true
rtn[“oauth_url”] = request_token.authorize_url

render json: rtn
end

[/bash]

ステップ2(Javascript)

ステップ1のソースのWindow呼び出しのところだけを抜粋。
RailsのAPIで戻ってきた認証用URLを引数にして window.open。

[bash]
windowLogin = window.open(response.data.oauth_url)
[/bash]

ステップ3

ここはプログラムする必要なしです。TwitterのOAuthの画面で
純粋に認証&許可をしてもらいます。

ステップ4(Rails)

Twitter側の認証と許可の処理が終わると、ステップ1.5で指定したコールバックURLに制御が戻ってきます。
テスト環境ですのでRailsのデフォルト http://localhost:3000 でサーバーが立ち上がっていて、
コールバックURLは http://localhost:3000/oauth_twitter です。

では、Railsの方の実装を見てみましょう。
最初にCONSUMER_IDとCONSUMER_SECRETを用いて、トークンを作成します。
さらに、パラメーターで渡ってきた、 oauth_tokenとoauth_verifierを使って
アクセストークンを得ます。
最後に得られたアクセストークンを使ってAPIアクセスし、ユーザー情報を得ます。

で、cookiesに得られた情報をセットして、このメソッドを抜けます。

cookies[:oauth_token] = params[:oauth_token]
cookies[:twitter_uid] = user_info[“id”]
cookies[:screen_name] = user_info[“screen_name”]

[bash]
class OauthController < ApplicationController #URL /oauth_twitter への処理 def twitter consumer = OAuth::Consumer.new( Oauth::TWITTER_CONSUMER_ID, Oauth::TWITTER_CONSUMER_SECRET, { :site => “https://api.twitter.com” }
)

request_token = OAuth::RequestToken.new(
consumer,
cookies[:request_token],
cookies[:request_token_secret]
)

access_token = request_token.get_access_token(
{},
:oauth_token => params[:oauth_token],
:oauth_verifier => params[:oauth_verifier]
)

response = consumer.request(
:get,
‘/1.1/account/verify_credentials.json’,
access_token, { :scheme => :query_string }
)

rtn = {}

case response
when Net::HTTPSuccess
user_info = JSON.parse(response.body)

if user_info[“screen_name”]
cookies[:oauth_token] = params[:oauth_token]
cookies[:twitter_uid] = user_info[“id”]
cookies[:screen_name] = user_info[“screen_name”]
else
#”Authentication failed”
end
else
#”Failed to get user info via OAuth”
end

end

end

[/bash]

RailsのGemfileに以下の3つのgemを追加します。

[bash]
gem ‘json’
gem ‘oauth’
gem ‘rack-cors’, :require => ‘rack/cors’

[/bash]

SPAをサーブしているサーバーとAPIをサーブしているサーバーが別物になるので、
クロスドメインなAPIアクセスとなるため、API側でそれを許可する必要があります。
gem rack-cors でそれに対応できます。で、対応するためにapplication.rbを
以下のように書き換えます。 本番ではoriginsは「*」ではなく、ちゃんと特定のドメイン名を入れるべし。

config/application.rb
[bash]
class Application < Rails::Application config.middleware.insert_before ActionDispatch::Static, Rack::Cors do allow do origins '*' resource '*', :headers => :any, :methods => [:get, :post, :delete]
end
end

end

[/bash]

ステップ5(RailsのJavascript)

OauthControllerのtwitterメソッドで処理をしたので、普通に処理が終われば、ポップアップ画面には
そのメソッドに対応するビューが表示されます。
なので、対応するビューは作るのですが、あくまでダミーで結構です。
そもそも、ポップアップ(別タブ)でTwitter認証が開いていて、認証が終わればポップアップは閉じてほしい
はずですので・・・

で、assets/javascripts 配下に以下のようなJavaScript(CoffeeScriptではない)を作ります。
ファイル名はなんでもいい。
で、ここでする処理は、ダミーのビューが表示された時にそのhrefをチェックして、
認証終了のダミービューだと判定できたら、window.closeしてやるというものです。

[bash]
$(function(){
if (window.location.href.indexOf(“oauth_twitter”) >0){
window.close();
}

});

[/bash]

ステップ6(SPAのJavascript)

最後にクライアントサイドです。window.openの時にsetTimeoutで呼ばれるメソッドです。
windowLogin.closedの状態を1秒間隔でチェックし、Twitterの認証画面が閉じたら
alertに認証で得られた情報をクッキーから表示します。(別に表示する必要はないのですが・・・)
で、認証が通ったと判定できたら、アプリのトップページなどへジャンプしてやります。

あとはSPAなのでAPIサーバーへアクセスするたびにおそらくユーザー認証も必ずすることになると思うので、
APIを呼ぶたびに、ここで得られた情報を付与してやるイメージでしょうか・・・

本題とはそれますが、setTimeoutで引数をつけたfunctionを呼びたいときは 無名関数とする
のがルールのようです。

[bash]
const CheckLoginStatusTwitter = (history) => {
if (windowLogin.closed) {
alert(Cookies.get(‘twitter_uid’) + “\n” + Cookies.get(‘screen_name’))
if (Cookies.get(‘twitter_uid’) != undefined && Cookies.get(‘screen_name’) != undefined){
history.push(“/counter”)
}
else{
alert(“認証に失敗しました”)
}
}
else{
setTimeout(function(){CheckLoginStatusTwitter(history)}, 1000)
}
}

[/bash]
oauth-14

あとがき

あっさりと説明しちゃいましたが、理屈がわかるまでも少し時間がかかりました。
SPAでSNS認証するならどうすりゃいいんだという情報が、以外になくて・・・
あちこち読み漁って、なんとか形になりました。
何かの参考になればと・・・・

About the author:

Related Posts

Leave a comment

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

Back to Top