SSRなNuxt、リロードするとstoreから認証情報が消えてしまい、
認証状態で初期レンダリングできないのつらいなとおもっていたら、
Nuxtのサンプルがよさげだったので、自分用に再整理してみた。
ざっくりの方針
- 認証情報をStoreに保存するときに、sessionにもいれておく
- sessionにいれるために、serverMiddlewareを使う
- serverMiddlewareではexpressを立てて、sessionに詰める/削除だけする
準備する。設定する。
ライブラリのインストール
npm install --save express express-session body-parser whatwg-fetch
serverMiddlewareの@/api/index.ts
serverMiddlewareで呼ばれる処理を書く。
セッションを詰める/削除するだけのAPIを用意。
import express from "express"; const router = express.Router(); const app = express(); router.use((req, res, next) => { Object.setPrototypeOf(req, app.request); Object.setPrototypeOf(res, app.response); req.res = res; res.req = req; next(); }); // API: /api/login router.post("/login", (req: any, res) => { if (!!req.body.authUser) { req.session.authUser = req.body.authUser; // セッションにつめる return res.json(req.body.authUser); // レスポンスはそのまま返す } res.status(401).json({ message: "Bad credentials" }); }); // API: /api/logout router.post("/logout", (req: any, res) => { delete req.session.authUser; // セッションの削除 res.json({ ok: true }); }); // Export the server middleware export default { path: "/api", handler: router };
store/index.ts
storeはこんな感じ。特別な点はactions
。
nuxtServerInit
でsessionに認証情報があれば、storeに入れるlogin
で認証情報をserverMiddleware
経由でセッションに保存logout
で認証情報をserverMiddleware
経由でセッションを削除
import { GetterTree, ActionContext, ActionTree, MutationTree } from "vuex"; import { Context } from "@nuxt/vue-app"; import Vue from "vue"; import axios from "axios"; // **************************************************** // * Types / interface // **************************************************** export interface User { uid: string; name: string; profileImage: string; } export interface Actions<S, R> extends ActionTree<S, R> { nuxtServerInit(context: ActionContext<S, R>, appContext: any): void; } export interface RootState { authUser: User | null; } // **************************************************** // * STATE // **************************************************** export const state = (): RootState => ({ authUser: null }); // **************************************************** // * MUTATIONS // **************************************************** export const mutations: MutationTree<RootState> = { SET_USER: function(state: RootState, user: User | null) { state.authUser = user; }, }; // **************************************************** // * ACTIONS // **************************************************** export const actions: Actions<RootState, RootState> = { async nuxtServerInit({ commit }, { req }) { // セッションがあれば、storeに詰める if (req.session && req.session.authUser) { commit("SET_USER", req.session.authUser); } }, async login({ commit }, { user }) { commit("SET_USER", user); // serverMiddlewareを呼び出してセッションに保存 await axios.post("/api/login", { authUser: user }); }, async logout({ commit }) { commit("SET_USER", null); // serverMiddlewareを呼び出してセッションを削除 await axios.post("/api/logout"); } };
nuxtconfig.ts
serverMiddleware
が有効になるように、nuxtconfig.ts
に設定を追加。
import NuxtConfiguration from "@nuxt/config"; import bodyParser from "body-parser"; import session from "express-session"; const config: NuxtConfiguration = { // ... 略 serverMiddleware: [ // body-parser middleware bodyParser.json(), // session middleware session({ secret: "super-secret-key", resave: false, saveUninitialized: false, cookie: { maxAge: 60000 } }), // Api middleware "~/api" ], }
つかう
使うときはよくある感じ。
midlleware/authed.ts
で未認証の場合はリダイレクトする処理を用意- 認証が必要なページで
midlleware:"authed"
を設定
midlleware/authed.ts
/** * 認証が必要なページで未認証の場合はリダイレクトする */ export default async function({ store, redirect }) { if (!store.state.authUser) redirect("index"); }
page/secret.vue
import { Component, Vue } from "nuxt-property-decorator"; @Component({ middleware: "authed" }) export default class SecretPage extends Vue { // ... 略 }
ほかにも
PWAやlocalstorateをつかったり、Cookieをつかったりいろいろあるっぽい。
ベターはPWAっぽい感じだけど、お手軽にできるのはserverMiddlewareっぽいので、
サンプルのをベースにしてみた♪
FirebaseAuthをカラメルと複雑になるするんので、どうするのがいいのかは悩む。。
まだまだ、試行錯誤中。。
以上!!
参考にしたサイト様
Nuxt
Firebase
事例/サンプル
Session
localstorage/vuex-persistedstate
その他