Nuxt 3では、Azure Static Web Apps(以下Azure SWA)にデプロイするための設定が用意されています。
しかしAzure SWAで提供されている機能を使おうとすると、そのままではいろいろと不都合な状態にあります。
こうした状態を解消するためのNuxtモジュールである、nuxt-swaライブラリを公開しました。(記事執筆時点でv0.5.0)
-
GitHubのリポジトリ
- IssueやPull Requestは随時受け付けています(日本語OK)
- 公式ドキュメント(兼Playground)
何が困るのか?
- 認証機能(
/.auth/...)などをサーバーサイドから呼べない - ルート規則がPage Routing時に機能しない
認証機能(/.auth/...)などをサーバーサイドから呼べない
Azure SWAでは、組み込みの認証機能を利用するAPIエンドポイントが以下のように提供されています。
-
/.auth/login/{認証プロバイダ名}: ログイン -
/.auth/me: ログイン中のユーザー情報(Client Principal)を取得 -
/.auth/logout: ログアウト
しかしながら、NuxtはSSR時に、内部API(/始まり)に対するfetchリクエストを全て内部実装の直接呼び出しに変換します。(Direct call)
そのため、以下のようなコードはサーバーサイド側で[Vue Router warn]: No match found for location with path "..."といったエラーを発生させます。
<template>
<button @click="navigateTo('/.auth/logout')">ログアウト</button>
</template>
<script setup lang="ts">
const { clientPrincipal } = await $fetch('/.auth/me')
</script>
また、現在パブリックプレビューであるData API Builderのエンドポイント(/data-api/配下)についても同様です。
ルート規則がPage Routing時に機能しない
Azure SWAは、特定のルートへのアクセスを認証済みユーザーのみに制限する機能も持っています。
{
"routes": [
{
// /profile 以下はログインユーザーのみ閲覧可能
"route": "/profile*",
"allowedRoles": ["authenticated"]
},
{
// /admin/index.html はadministratorロールを持つユーザーのみ閲覧可能
"route": "/admin/index.html",
"allowedRoles": ["administrator"]
},
{
// /api/ 以下のPUT, POST, PATCH, DELETEリクエストはadministratorロールを持つユーザーのみ
"route": "/api/*",
"methods": ["PUT", "POST", "PATCH", "DELETE"],
"allowedRoles": ["administrator"]
}
]
}
しかし、これが機能するのはAzure SWAに対して直接リクエストを発行した場合のみになります。
そのためクライアント側でルーティングを実施した場合(例: Vue Router)や、前述のようなAPIのDirect callが発生した場合は、認証がスキップされてしまいます。
nuxt-swaが提供する機能
下記は一例ですので、ほかの機能についてはドキュメントをご覧ください。
SWA組み込みAPIに対するプロキシ
/.auth/以下および、/data-api/以下のルートにリクエストがあった場合、ドメインを付与したフルパスにプロキシするイベントハンドラーを組み込んでいます。
「Azure Functions側で付与される
x-ms-original-urlを読み取ることでドメインを判別する」仕組みなので、サーバー側API(/serverフォルダ配下)から$fetchを呼び出す際はリクエストヘッダーを明示的にバイパスする必要がある点に留意してください。
Global Page Middleware
Pageコンポーネントで以下のように設定することで、モジュール内部で指定したロールを持っていない場合はレンダリング前に401/403エラーを返します。
<script setup lang="ts">
definePageMeta({ allowedRoles: 'authenticated' })
</script>
<template>
<div>Private Page!</div>
</template>
既存の
staticwebapp.config.jsonとの併用も可能ですが、初期リクエスト時(Azure SWAのエラーページ)とページルーティング時(Nuxtのエラーページ)で表示されるエラーページが変わってしまうため、上記設定に寄せることをオススメします。
組み込み認証を利用するためのComposable関数
const {
clientPrincipal, // 認証情報
isLoggedIn, // ログインしているかどうか
hasRole, // ロールを持つかどうかを返すcomputed<boolean>を生成する関数
login, // navigateTo(`/.auth/login/{provider}`)
logout, // navigateTo(`/.auth/logout`)
} = await useEasyAuth()
サーバー側でユーザー情報を取得するユーティリティ関数の提供
export default defineEventHandler(event => {
// リクエストしたユーザーの認証情報を取得
const clientPrincipal = getClientPrincipal(event)
})
export default defineEventHandler(event => {
// administratorロールを持つか
if (!hasRole(event, 'administrator')) {
throw createError({ statusCode: 403 })
}
})
nitro.azure.configに対する型定義の提供
上記設定がない場合は、モジュール側でも警告します。
今後の展望(ToDo)
- Nuxt Modulesへの登録
-
async/awaitを使うとNuxtコンテキストを失ってしまうことがあるらしいので、構造を再検討する - 型定義がうまく反映されない(特に/server側)ので、適切に設定する
- インテグレーションテストの整備
- Azure SWA環境でないと厳しいかも
- ビルド後に
staticwebapp.config.jsonを/.output/publicに移動させるオプションを用意
