こんにちは!kossyです!
さて、今回はAuth0のログイン機能で取得したaccess_tokenを使ってRails側で認証を通してみたので、
備忘録としてブログに残してみたいと思います。
環境
Vue
@vue/cli 4.5.9
vue@3.0.5
npm 6.14.8
node 14.15.0
Rails側での準備
以下のAuth0の中の人が書いたチュートリアルを参考にRailsのプロジェクトを作成しました。
auth0.com
ブログに書いてないもので別途対応したことは、CORSの対応です。
今回はVue.jsからRailsAPIへCROSS ORIGINな通信を行いたいので、rack-cors gemを導入します。
# Gemfile get 'rack-cors' # terminal $ docker-compose run web bundle install
config/initializers/cors.rbを編集します。
Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins '*' resource '*', :headers => :any, :expose => ['access-token', 'expiry', 'token-type', 'uid', 'client'], :methods => [:get, :post, :options, :delete, :put, :patch] end end
今回はoriginsを * (どこのオリジンからの通信でも許可する)にしていますが、この場合だとSameOriginPolicyが意味を為さなくなってしまい、
XSSやCSRFに対して脆弱になってしまうので、実際に運用するときは環境変数にオリジンを格納する等して、
ワイルドカードではなくきちんと指定してあげるようにして下さい。
参考: なんとなく CORS がわかる...はもう終わりにする。 - Qiita
また、chirps_controller.rbのskip_before_action :authorize_request, only: [:index, :show]もコメントアウトするようにしてください。
app/controllers/chirps_controller.rb
# skip_before_action :authorize_request, only: [:index, :show]
これでRails側の準備は完了です。
クライアント側の準備
以前執筆した拙著の中で作成したAuth0での認証機能が追加されたVue.jsアプリをクライアントとして使います。
kossy-web-engineer.hatenablog.com
src/router/index.tsに以下のコードを追記します。
// 省略 import { routeGuard } from '@/auth' const routes: Array<RouteRecordRaw> = [ { path: '/', name: 'Home', component: Home, beforeEnter: routeGuard // 追加 }, // 省略
beforeEnterプロパティにrouteGuard関数を指定することで、ルートパスにアクセスがあった場合にコンポーネントの描画が行われる前に認証を要求されるようになります。
また、HTTP通信を行いたいため、今回はaxiosを導入します。
$ npm i axios
$ mkdir src/api $ touch src/api/client.ts $ touch src/api/chirp.ts $ touch .env.development
// src/api/client.ts import axios from 'axios' export default axios.create({ baseURL: process.env.VUE_APP_API_BASE })
import Client from '@/api/client' export const getChirps = async (token: string) => { return await Client.get('/chirps', { headers: { 'Content-Type': 'application/json', Authorization: `bearer ${token}` } }) .then((response) => { return response.data }) }
// .env.development VUE_APP_API_BASE=localhost:3000
client.tsはaxiosインスタンスをbaseURL付きで生成するコードになっています。
axiosを直接コールするのではなく、chirp.tsでimportしClient.get(...)のように呼び出せるようにします。
ライブラリをラップしてあげることで、仮にライブラリを差し替えることになっても、修正範囲を少なくすることができます。
chirp.tsはlocalhost:3000/chirpsを叩く関数を定義しています。
引数でtokenを受け取って、HTTPリクエストヘッダーに載せる形で用います。
.env.developmentはVue.jsで環境変数を扱う場合に用いるファイルです。
process.env.VUE_APP_API_BASEのように記述することで、ファイル内で環境変数を使うことができます。
次はgetChirps関数を叩くためにsrc/views/Home.vueを修正します。
// src/views/Home.vue <template> <button @click="handleGetChirps">Get Chirps</button> // 追加 <div v-if="!auth.loading.value"> <button v-if="!auth.isAuthenticated.value" @click="handleLogin">Log in</button> <div v-if="auth.isAuthenticated.value"> <button @click="handleLogout">Log out</button> <p>{{ auth.user.value.name }}</p> </div> </div> </template> <script lang="ts"> import { defineComponent, inject, reactive } from 'vue' import { getChirps } from '@/api/chirp' // 追加 export default defineComponent({ name: 'Home', setup () { const auth = inject<any>('$auth') const handleGetChirps = async () => { // 追加 const res = await auth.getTokenSilently() // 追加 await getChirps(res).then((res) => { // 追加 console.log(res) // 追加 }) // 追加 } // 追加 return { auth, handleGetChirps, // 追加 handleLogin: () => { auth.loginWithRedirect() }, handleLogout: () => { auth.logout({ returnTo: window.location.origin }) } } } }) </script>
getChirps関数をimportして、クリックイベントをトリガーに関数を叩くようにしました。
await auth.getTokenSilently() は、Auth0クライアント内に保存されているTokenを取得しています。
正しく認証をパスしていれば、getTokenSilently()を呼び出すとTokenが取得できます。
これで動作確認の準備は完了です。
動作確認
まず、localhost:8080にアクセスして、認証を要求されるか検証してください。
beforeEnterが正常に機能していれば、添付画像の画面が表示されます。
認証に成功したら、以下のような画面になります。
ここで、検証ツールを開いてから、Get Chirpsボタンを押してみましょう。
通信に成功すれば、以下のようにレコードが取得できるはずです。
先ほど、Rails側でskip_before_actionをコメントアウトしているため、chirps関連のレコードにアクセスする場合は認証を要求されるようになっているので、
認証に失敗するとHTTPステータスコード「401」が返り、成功すると「200」と共にchirpsのレコードが返ります。
検証ツール(Chromeを想定しています)で添付画像のようにNetworkタブに合わせて、XHRを選択してください。
この状態で再度Get Chirpsボタンを押してみましょう。
問題なく認証をパスすれば、上記のような画面になります。
chirpsをクリックすると、通信の詳細を確認することができます。
きちんと値を取得できていることがわかります。