2019.11.11
Vueでカクテルデータベースをリファインする話#20【管理画面をつくろう#3】
永遠におわらない認証のはなし。前回見つけたプロジェクトファイルがいい感じだったので、これを完璧にコピーすればしばらくは安全ですね。
目標としては
僕のtwitterアカウントでユーザー登録をし、ログアウトまでできるっていうのを目標にします。それができれば大概のパターンに対応できると思うので。
とはいえ、まずは通常のメールアドレスを用いたログインですね。順番に
cretueusebiu/laravel-vue-spa
を読み解いていきましょう。
まず、app.jsに関しては手を加える必要はなさそうです。これは
よくわからんjsライブラリを用いていないということですね。Laravel標準のAPI認証を使っている、って感じかな。
まずはRegister部から見てみましょう。
エスケープがめんどいのでもうスクショで(笑)
これを呼んでいくわけですが、早速
middleware:guestっていうよくわかんない記述がありますね。
やりたいこととしては
未認証の時のみ読み込まれるファイルっていうのを明示化してるんだと思うんですが、その実体が不明ですね。importしてるわけでもないし、グローバルでなにか読み込んでるわけでもなさそう。ルーティングにも疑わしいものがない・・・・・・。
一応
Nuxtの機能で見つけたんですが、このプロジェクトNuxt使ってんの・・・・・・?
Vueでそれっぽいものは見つからなかったので、このあたりは
ナビゲーションガードで代替することにしますかね。
登録の実体はregister()ですね。async/awaitのいい使用例ですね。ここでapi/registerに処理が移ります。
失敗した時はどこでcatchするんだろう。クラッシュさせんの・・・・・・?
初回登録時はEメールを認証させるとのことでなんかフラグ入れてますね。その後どうなるんだろう。
それ以外の場合はふつうにログインします。フォームごとapi/loginに投げ、tokenを取得します。この代入の仕方はちょっとよくわからないんですが、調べてみたら
分割代入という記法らしい。とりあえず、tokenが欲しいみたいですね。
で、ストアにトークンを保持して、ユーザー情報をアップデートし、ホーム画面に飛ばす、と。
このあたりはまた後で見るとして、次はapi側、つまりlaravel側を読みたいですね。
ルーティングは割愛してRegisterControllerです。なんでフォームバリデーション使ってないんだろう。
use RegisterUsersで手抜きしてますね。トレイトを使ってるので、その内部は見に行かないとわかんないんですが、中身まで見る必要は現時点ではないのでスルーです。
コンストラクタでミドルウェアを呼んでて、ゲスト時にのみ読み込まれる、みたいなことをしてそうですね。
バリデータを自分で定義(たぶんオーバーライド)するんですが、ここでバリデータを定義するからフォームリクエストを使う必要がないor使えないってことなんでしょうか。
バリデーションを通過したらcreateでユーザーを作り、registeredが呼ばれ、新たに生成されたユーザーインスタンスも読むことができそうですね。
MustVerifyEmailは今回必要ないんですけど、これどこで設定してるんですかね。DBのデフォルト値?まぁ本題とはずれるんでおいときましょう。
なんかNotification呼んでますけど、Userクラスを見てみると
通知なる機能を使ってるみたいです。これは僕使ったことないですね・・・・・・。
どうやらメールとかで通知するのを一行でぱぱっとできそうですね。どこまでを自分で実装する必要があるのかが不透明なんですが、参照するべき項目はわかったのでこれもスルー。
とりあえず最終的にjsonを返すみたいですね。中身はuserクラスのfillable部分だと思います。EmailVerificationが必要な場合はステータスだけを返すって感じらしい。メールにトークン埋め込んでごにょごにょやってんのかな。
まぁこのへんは自分でも実装できそうな香りはする。
登録は簡単そうですね。つぎはログインです。
コード自体は似てますね。
api/loginにフォームをpostしてるだけなので、認証関係は全部laravel側でやってるっぽい。こういうのが理想だったんですよね。
で、受け取ったトークンとrememberをVuexに保存するわけですが、この時点ではユーザー情報は取ってこれてないってことかな?
auth/fetchUserでユーザー情報をフェッチさせてるっぽい。このへんは見てみたいですね。とりあえず先にAPI側を見てみましょう。
ルーティングはさっき省略してたんですが、ひとつだけ補足しておくと、こんなふうにミドルウェアを噛ませて保護してます。
認証状態はLaravel側で持つことができてるっぽいので管理も非常にラクですね。今回は未認証状態でloginにアクセスするので、LoginControllerを読みに行けばいいわけですね。
長いなあ・・・・・・(笑)
コントローラー自体にももちろんミドルウェアでの保護が入ってるんですが、exceptを使うことで除外できるんですね、なーーーるほど。今までルートだけで満足してコントローラーに保護ってあんま加えてなかったから知らなかった・・・・・・。
loginメソッドはトレイトから引っこ抜いてるっぽいので挙動がわかりませんが、重要そうなのは
attemptLogin()ですね。おそらくここが呼ばれてます。
$tokenってのはおそらくアクセストークンのことですが、guard()がなにやってるか全くわかんないですね。credentialsもよくわかりませんが、多分これをオーバーライドすると、例えばIDorEmailでの認証ができるようになると思います。
で、トークンの取得に失敗したらfalseを返すんですが、その次の行で平然とユーザー情報取ってきてますね。guard()の中身がわからんからなんでこれでアクセスできてるのかよくわからん。
とりあえず、まだEmailの認証が終わってなかったらこれもリジェクトして、大丈夫だったらトークンをセットするみたいですね。
で、その下のヤツらが、それぞれの場合に返すレスポンスの実体ですね。
ログインに成功したらアクセストークンというか、jwtを返し、失敗したら例外を投げる、と。
相変わらずこの例外はどこで受け取ってるんですかね・・・・・・。laravel側がなんかを返すんでしょうけど、vueファイルの中に受け取ってる場所が見当たらんのだが・・・・・・。
とりあえずわからんので置いといて、次はVuexですね。なんかわけわからん書き方してますね・・・・・・。
トークンはクッキーに埋め込んでるみたいですね。なんでかわかんないけど、Laravel側がクッキーからしか読み込めないからなのかな。ローカルストレージとかは通信できないと思うんで・・・・・・
気になるfetchUserは、api/userから読み取ってます。これは認証済みルートですね。
なんとこんな雑に取れちゃいます。フレームワーク様様ですね・・・・・・。
うまくユーザー情報が取れればストアにぶちこんでおしまいですね。asyncなのでPromiseを返して終わりって感じです。
このストアに関しては丸コピで良さそうなんですが、最後の
fetchOauthUrl()が謎ですね。こんなん呼んでたっけ?
ただ、oauthってことはSSOで呼んでる可能性が高いですね。
ログアウトに関しては大した処理ないしいいや・・・・・・
次にSSOですね。これも登録から見ていきますかね。
LoginWithGithubですね。ボタンごとコンポーネントとして切り取られているみたいです。クリックするとloginが発火します。
さて、ここの$tってなんですかね・・・・・・?たぶん多言語対応ライブラリのあれかな、使い方を見ると・・・・・・新しいウィンドウを開いてるっぽいんですが、引数はタイトルバーとかですかね?って思って調べたらそのとおりだったので、$tは言語に関するシングルトンっぽいですね。
次のurlでさっき謎だったVuexのアクションが使われてるので、やはりそういうことだったみたいです。ここで任意のプロバイダーに対応するAPIを発行するわけで、今回はgithubのAPIを覗きに行きます。
なかなか重量のあるコントローラーが出てきましたね(笑)
呼び出されるのは
redirectToProvider()です。
そこでSocialite呼んでますね。その実体に関してはフレームワーク側の話なので置いといて、それに/callbackがついて返却されるので
handleProviderCallbackを読みます。
ソーシャルログイン自体は昔に実装したことあるんですけど、ユーザーデータとかの取り扱いってこのコールバックでやって良かったんだっけなぁ・・・・・・。例えばFacebookとかだと氏名とか取れるんですけど、ここでDBについでに登録するみたいなの・・・・・・は、下の方にあるcreateUserで定義すれば済みそうですね。やらかしぴーーーや^^
findOrCreateUserっていうのを呼んでてそこでごにょごにょやってそう。ユーザーを一致させるエビデンスはメールアドレスかな。これユーザーアカウントが分離したらどうしようなぁみたいな対策も考えないとですね〜。
とりあえずトークンも得られますし、欲しい情報全部コミコミでoauth/callbackにかえしてあげます。
ただこれ気になるんですけどビューを呼び出してますね。どういうことだろう・・・・・・?見に行ってみますか。
なぁ〜〜〜〜にこれ...............
えーと、外部サイトへのアクセスは新ウインドウを開いてやってたわけなのでそれを閉じるついでにトークンを保存したいって感じかな。この構文の仕様に関しては
javasciptのドキュメントを見れば理解できますね。とりあえず今開いてるウィンドウで持ってるtoken値を、このアプリケーションのルートを開いてるウインドウに渡しますよ〜っていうやつらしい。
で、このウインドウはもう要らないので閉じちゃえと。
んで、最後の疑問なんですけど、"/"っていつ開いたんですかね?っていう。
怪しいのはコレなんですが、このonMessageはいつ呼ばれたのか?っていう話になります。
で、それはこのイベントリスナーを解読すればわかるようになります。メッセージをハンドラとしてonMessageを発火するわけで、先程別ウインドウからpostMessageなるものでtokenを流し込んでるのでonMessageが発火される・・・・・・っていう流れのはずなんですが、じゃあurl("/")ってなんだ・・・・・・?
vue-routerによってurlは見た目上書き換えられているけど、その実体(ブラウザが認識しているURL)は依然ルートディレクトリのまま、ってことになるのか・・・・・・?
まぁなんか問題あったらここは書き換えれば済みそうなのでそれで良しとしますか。
全貌は掴めたと思うので、これを移植すればよさそうですね。普通の認証は簡単そうなのでtwitter認証だけサクっと実装しておしまいにしようかな。twitterはどうせ辞めることないんだろうし(笑)
あ、そういえばアカウント凍結されたらどうするんだろう?まぁなんとかするしかないっすね 連携しているメールアドレスが不通にならない限り救済はがんばれば可能だと思うし。
ってなわけで、こいつをガリガリ実装していきましょーーーか。