LoginSignup
1
0

More than 1 year has passed since last update.

Nuxt2 + express + express-openid-connect の認証について

Posted at

はじめに

Nuxt.jsでSSRを使ったアプリケーションを作成する時に、express-openid-connectを使った認証を実装する際につまづいた部分があったので、自分自身の備忘録として、また同じ所でつまづいた人に対して、何かのヒントになればと思い記事を書きます
備忘録で大事な部分は「🔑」注意する部分は「🚨」を記載します

記事の内容としては実際にNuxtのアプリケーションを作りながら説明をしていく流れになります
🚨 Nuxt2でのアプリケーション作成となります

主な技術

node : v16.14.2
npm : v8.5.0
yarn : 1.22.18
Nuxt : 2.15.8
Express : 4.18.1

ここでは書かない事

Nuxtの細かい設定・操作
Expressの細かい設定・操作
OAuth自体の設定

認証までのフロー

Nuxtで認証を行うのはバックエンドとフロントエンドそれぞれ、認証させている事を認識させる必要があります。
🚨 フローの流れはあくまでイメージになります

スクリーンショット 2022-05-09 15.07.09.png

Nuxtプロジェクトの作成

作りたいディレクトリ上でプロジェクトを作成します
今回は「oauth-app」というアプリケーション名とします

公式:https://nuxtjs.org/docs/get-started/installation/

yarn実行後、色々と質問をされるので、 基本は全て、Enterで良いですが、以下の質問は下記のように選択して下さい

  • Nuxt.js modules: Axios - Promise based HTTP client (スペースキーで選択)
  • Rendering mode: Universal (SSR / SSG)
  • Deployment target: Server(Node.js hosting)
yarn create nuxt-app oauth-app

処理が終わりましたら、問題なく起動できるか確認をします。
🔑 dockerで環境を作っている場合、「nuxt.config.js」のserverの追記を忘れずに

cd oauth-app
yarn dev

起動が、ブラウザでアクセスをして画面が表示されればプロジェクト作成は完了です
説明上は「localhost:3000」とします
スクリーンショット 2022-05-09 13.22.25.png

BFFの設定

まずはバックエンドの設定、準備を行います

Expressの導入

BFFを使用するために Express をインストールします

yarn add express

「nuxt.config.js」に「serverMiddleware」の設定を追加
https://nuxtjs.org/docs/configuration-glossary/configuration-servermiddleware/

nuxt.config.js

serverMiddleware: [
  '~/server'
]
...

プロジェクト内にフォルダと以下のファイルを作成

server / index.js
import express from 'express'
const app = express();

//動作確認用 確認できたら消しても良いです。
app.get("/test", function(req, res){
  res.json({"value" : 1});
});

module.exports = {
  path: '/api',
  handler: app,
}

再度、Nuxtを起動して以下のURLをブラウザアクセスし、表示されればExpressの導入は完了です
http://localhost:3000/api/test

{"value":1}

express-openid-connect の導入

今回の本命である express-openid-connect をインストールしていきます
https://github.com/auth0/express-openid-connect

yarn add express-openid-connect

インストール後、「server / index.js」に下記を追記
🔑 configの値は環境変数から取得する方法が良い

server / index.js
import express from 'express'

const {auth, requiresAuth } = require('express-openid-connect'); //追加

//追加
//値等は公式ドキュメントを参考に設定
//https://auth0.github.io/express-openid-connect/interfaces/configparams.html
const config = {
  authRequired: false,
  baseURL: '<baseURL>',
  clientID: '<clientID>',
  issuerBaseURL: '<issuerBaseURL>',
  secret: '<secret>',
  clientSecret: '<clientSecret>',
  idpLogout: true,
  authorizationParams: {
    response_type: '<response_type>',
    scope: '<scope>',
    audience: '<audience>',
    prompt: '<prompt>',
    redirect_uri: '<redirect_uri>/api/auth/callback',
  },
  routes: {
    login: false,
    callback: '/auth/callback',
  },
  afterCallback: async (req, res, session, decodedState) => {
    return {
      ...session,
    };
  }
};

const app = express();

app.use(auth(auth_config)); //追加

//追加
app.get('/login', (req, res) => {
  res.oidc.login({
    returnTo: '/callback'
  });
});

//追加
app.get('/callback', requiresAuth(), (req, res) => {
  const token = req.oidc.accessToken.access_token
  res.json({
    token: token,
    login: true
  });
});

//動作確認用
app.get("/test", function(req, res){
  res.json({"value" : 1});
});

module.exports = {
  path: '/api',
  handler: app,
}

🔑 認証後のパスの変更方法
OAuthで認証後、でフォルデのリダイレクトするパスは「/callback」になります。Expressのみでしたら、問題ないのですが、今回はNuxtでのアプリケーションなので「/callback」の場合フロントを表示するパスになります。その場合、認証情報を「express-openid-connect」がいい感じに保持してくれません。このパスを変更する場所が、公式ドキュメントも、Gitを見ても分からず結構時間を要しました。結論、「authorizationParams.redirect_uri」で指定することで変更することができました。

設定後、Nuxtを起動して以下のURLをブラウザアクセスし、認証画面の表示
http://localhost:3000/api/login

※イメージ
スクリーンショット 2022-05-09 18.30.35.png

認証後に以下のURLに遷移して画面が表示されればオッケー!
http://localhost:3000/callback

スクリーンショット 2022-05-09 18.30.46.png

これで、認証とBFF側の認証保持ができている状態になります

確認として、以下のURLにアクセスしてみて下さい
以下のように表示されれば認証の保持はできています
http://localhost:3000/api/callback

{"token":".....","islogin":true}

📍 ログアウトする場合は以下のURL
http://localhost:3000/api/logout

フロントエンドの設定

フロントエンドで認証していなければ表示できない画面を作っていきます

store の作成

フロントエンドの状態を保持するためのstoreを作成します
https://vuex.vuejs.org/ja/

プロジェクト内にフォルダと以下のファイルを作成

store / index.js
export const state = () => ({
  auth: false,
});

export const mutations = {
  login(state) {
    state.auth = true;
  },
  logout(state) {
    state.auth = false;
  },
};

middleware の作成

表示したいページが認証をしていないと表示できないページがどれかを設定していきます
指定したページ('/','/callback')以外は全て認証が必要という設定にします

「nuxt.config.js」に以下を追加

nuxt.config.js
router: {
    middleware: 'session'
  },
...

プロジェクト内にフォルダと以下のファイルを作成

middleware / session.js
export default ({ store, route, redirect }) => {
  if (!store.state.auth && !['/', '/callback'].includes(route.path)) {
    return redirect('/')
  }
}

page の作成

認証情報を取得するページ、認証後に表示するページを作っていきます

下記二つのファイルを作成

pages / callback.vue
<template>
  <p>Please Wating・・・・</p>
</template>
<script>
export default {
  async mounted() {

    await this.$axios.get("/api/callback")
    .then( responce => {
      console.log("responce",responce);
      this.$store.commit("login");
      this.$router.push("/home")
    })
    .catch(error => this.$router.push("/"));
  },
};
</script>
pages / home.vue
<template>
  <div>
    <p>認証画面</p>
    <button @click="logout">Logout</button>
  </div>
</template>
<script>
export default {
  methods: {
    logout(){
      console.log('logout')
      this.$store.commit("logout");
      window.location.href = "api/logout";
    }
  }
}
</script>

以下のファイルを編集

page/index.vue
<template>
  - <Tutorial/> //削除
  <div>
    <p>ログイン画面</p>
    <button @click="login">Login</button>
  </div>
</template>
<script>
export default {
  name: 'IndexPage',
  methods: {
    login(){
      window.location.href = "api/login";
    }
  }
}
</script>

作成後、Nuxtを起動して以下のURLをブラウザアクセスし、「login」ボタンからログイン後、「/home」のパスに遷移すればオッケーです
http://localhost:3000

また、「/home」の画面にある、「logout」ボタンをクリックすると「/」パスに遷移すればオッケーです

ログインしていない状態で、以下のURLにアクセスしても「/」に移動することも確認してください
http://localhost:3000/home

認証の永続化

この状態だと、認証後に「/home」パスでブラウザをリロードすると「/」に遷移してしまいます
これは、フロントで保持している store が session で保持しているのでリロードをすると値が初期化してしまいます
そのため、毎回「/」パスに遷移してしまっているという事です
なので、リロードしても store の値を保持するようにしていきます

vuex-persistedstate の導入

store の保存先を session ではなく、 localstrage や cokkie に保存するためのパッケージになります
ブラウザをリロードしてもデータを保持するパッケージをインストールします
https://www.npmjs.com/package/vuex-persistedstate

yarn add vuex-persistedstate

js-cookie の導入

今回は、 cookie に保存するため以下のパッケージもインストールします
https://www.npmjs.com/package/js-cookie

yarn add js-cookie

プロジェクト内にフォルダと以下のファイルを作成

plugins/persistedstate.js
import createPersistedState from 'vuex-persistedstate';
import * as Cookies from 'js-cookie';
import cookie from 'cookie';

export default ({ store, req }) => {
    createPersistedState({
        key: 'oaut_data',
        storage: {
            getItem: (key) => {
                // See https://nuxtjs.org/guide/plugins/#using-process-flags
                if (process.server) {
                    const parsedCookies = cookie.parse(req.headers.cookie);
                    return parsedCookies[key];
                } else {
                    return Cookies.get(key);
                }
            },
            // Please see https://github.com/js-cookie/js-cookie#json, on how to handle JSON.
            setItem: (key, value) =>
                Cookies.set(key, value, { expires: 365, secure: false }),
            removeItem: key => Cookies.remove(key)
        }
    })(store);
};

「nuxt.config.js」に以下を追加

nuxt.config.js
plugins: [{ src: "~plugins/persistedstate.js", ssr: true },]
...

これで、ブラウザをリロードしてもフロントも認証情報は保持したままになります

これでバックエンドとフロントエンドの認証の設定は完了になります

さいごに

OAuthのつまづいた部分もそうなんですけど、同時にNuxt全体の仕様などの振り返りにもなったと思っています

以上

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0