🔒

Nuxt 3 の Basic 認証モジュールを作成する

2022/08/07に公開

Nuxt 3 は Nuxt 本体の機能拡張を行う "モジュール" の作成がとても簡単になりました。

便利なツールが豊富に用意されていますので、ぜひみんなで Nuxt のエコシステムを大きくしていきましょう。

Nuxt Basic Authentication Module について

Nuxt は Nitro により http サーバーを含んだ形でアプリケーションをビルドすることができます。
SSR(ISG) をするにあたっても Express 等を用意する必要がなくなりました。

そのため本番環境・ステージング環境とも Vercel や Netlify のような環境のみで用意する人も多いのではないでしょうか。

Nuxt Basic Authentication Module は「ステージング環境でBasic認証をかけたい」というときに、とても役に立つモジュールです。

https://www.npmjs.com/package/nuxt-basic-authentication-module

※ Nuxt 3 専用です

使い方

nuxt.config.ts にて Nuxt Basic Auth Module を読み込み、必要に応じてオプションを設定すると、自動的に Basic 認証がかかります。

nuxt.config.ts
import { defineNuxtConfig } from 'nuxt'
import BasicAuth from 'nuxt-basic-authentication-module'

export default defineNuxtConfig({
  modules: [
    [BasicAuth, { enabled: true }],
  ],
  runtimeConfig: {
    basicAuth: {
      productionDomains: [
        'www.example.com',
      ],
      pairs: {
        foo: 'bar',
      },
    },
  },
})

RuntimeConfig で設定した内容は、クライアントには渡りません。
ソースコード上に「ユーザー・パスワードの組み合わせ」 pairs を記載したくない場合は、環境変数でセットしてください。

何も設定しなければ admin: admin になります。

productionDomains は本番環境のドメイン(Basic認証をかけないドメイン)を指定します。
ドメインを endsWith でチェックしているので example.comfoo.example.com にもマッチします。
また bar.example.comtest--bar.example.com にもマッチしますので、ご注意ください。

全体的に認証をかけたり外したりする場合は enabled オプションで設定することができます。
enabledfalse の場合は serverMiddleware がアタッチされません)

Nuxt モジュールの作り方

基本的な Nuxt 3 モジュールの作り方は、公式ドキュメントに詳しくまとまっています。
https://v3.nuxtjs.org/guide/going-further/modules

Nuxt モジュールはプラグインと違い、ビルドのタイミングで Nuxt や Vue.js の挙動を操作することができます。

Basic 認証は serverMiddleware と同等の挙動を期待しています。
(Nitro の Plugin として構築することもできそうですが、今回は Nuxt の RuntimeConfig でオプションの設定を行うべく、Nuxt モジュールとして構築しました)

下準備

まずはローカル環境に、リポジトリを作成します

npx nuxi init -t module my-module

できあがった my-module フォルダ内は次のようになっています

  • playground
  • src

src フォルダが開発するプラグインのソースコード用です。
playground フォルダは、開発中のモジュールの動作確認が行える(設定済みの)環境です。

playground 内で npm run dev すれば Nuxt が起動し、モジュールがビルド時にプラグイン (src/runtime/plugin.ts) をアタッチします。
ブラウザのコンソールに Plugin by my-module! と表示されていることでプラグインが動作していることがわかります。

モジュール本体のソースコードは src/module.ts です。

オプションを設定できるようにする

モジュールのオプションは playground/nuxt.config.ts のように設定可能です。
(配列の2つ目の要素にオブジェクトで設定)

src/module.tsdefineNuxtModule() 内で setup() の第1引数として取得できます。
defaults の内容が初期値としてマージされますので便利です。

メインの module.ts を作成する

今回は Plugin の利用はありません。
忘れずに src/runtime/plugin.ts を削除しておきましょう。

今回は enabled オプションのみとし、そのほかの設定は RuntimeConfig を使用します。
なお、モジュール作成に必要な機能は @nuxt/kit から import することができます。

src/module.ts
import { resolve } from 'path'
import { fileURLToPath } from 'url'
import { defineNuxtModule, addServerHandler } from '@nuxt/kit'

export default defineNuxtModule<ModuleOptions>({
  meta: {
    name: 'basic-auth',
    configKey: 'basicAuth',
  },
  defaults: {
    enabled: true,
  },
  setup (options, _nuxt) {
    if (!options.enabled) {
      return
    }
    const runtimeDir = fileURLToPath(new URL('./runtime', import.meta.url))
    addServerHandler({
      middleware: true,
      handler: resolve(runtimeDir, 'server/middleware/auth'),
    })
  },
})

serverMiddleware を作成する

src/runtime 以下に auth.ts を作成します。
(今回は src/runtime/server/middleware/auth.ts としました)

src/runtime/server/middleware/auth.ts
import { defineEventHandler } from 'h3'
import auth from 'basic-auth'
import { useRuntimeConfig } from '#imports'

export default defineEventHandler((event) => {
  const {
    pairs = { admin: 'admin' },
  } = useRuntimeConfig().basicAuth || {}

  const { name, pass } = auth(event.req) ?? {}
  if (!name || !pass || !pairs[name] || pairs[name] !== pass) {
    event.res.statusCode = 401
    event.res.setHeader('WWW-Authenticate', `Basic realm="Authentication Required"`)
    event.res.end('Unauthorized')
  }
})

ヘッダーの情報は basic-auth という npm パッケージを使用し取得しました。
https://www.npmjs.com/package/basic-auth

利用者の RuntimeConfig は #imports よりインポートする useRuntimeConfig() で取得します。
(利用者の .nuxt/imports.d.ts に記載されている node_modules/nuxt/dist/app よりインポートされることになります)

※その他のオプションについてはこの記事では説明を省略します。

TypeScript の型情報を追記する

TypeScript の型を書いておくと、モジュールのビルド時に書き出してくれます。

src/module.ts
declare module '@nuxt/schema' {
  interface RuntimeConfig {
    basicAuth: {
      pairs?: Record<string, string>
    }
  }
}
export interface ModuleOptions {
  enabled?: boolean
}

Netlify で使ってみる

上記のソースコードではすべての機能を記載していませんが、以上で一旦完成です。

Netlify は GitHub に push した内容で自動的にソースコードのビルドとサイトの公開が可能です。
設定によりメイン以外のブランチでも、コミットごとにステージング環境を用意してくれます。

(今回の記事では記載していませんが)用途としてはステージング環境(のみ)で動作してほしいモジュールですので、実際に Basic 認証がかかっているかを確認してみてください。

npm パッケージとして公開する

その他のオプションや、テストを記載したら、npm パッケージとして公開します。
(すでにビルド等の設定がなされているので煩わしい準備は不要です)

NPM への公開は npx np コマンドが便利です。
(バージョン番号の管理から GitHub のリリースノートの下書き作成までやってくれます)

無事公開されたら npm i -D nuxt-basic-authentication-module を、自分のプロジェクトの環境で実行します🎉
(本来の手順としては公開前に GitHub のリポジトリから直接インストールして動作確認となります)

おわりに

いちから調査しつつ構築しましたが、丸2日程度でできました。

なにより @nuxt/kit が便利すぎます。
便利な機能を開発したら、再利用するためモジュールにしていきましょう。

そして、よろしければぜひ他の Nuxt ユーザーのために公開してください。
Nuxt のエコシステムが広がっていくことを期待しています。

Discussion