この記事を読んで使ってみたいなぁと思い、はや数ヶ月
ちょっと試してみようと、いろいろ調べてみたときの備忘録(*´ω`*)
Nuxt Auth Utilsとは
https://github.com/atinux/nuxt-auth-utils
- 認証周りのライブラリ
- クライアントサイド、サーバサイドを含めSSRに対応
- 20以上OAuthプロバイダーに対応
- ※
nuxt build
のみ、nuxt generate
は非対応
Nuxt Auth Utilsが提供しているもの
- ユーザセッション周りのServer UtilsやComposable&Component
- Server Utils(
setUserSession()
、etc..) ... 保存/取得クリアなどのセッション管理系 - Composable(
useUserSession()
) ... 現在のログインユーザ関連の情報 - Component(
<AuthState>
) ...loggedIn
やclear
などを受け取れるコンポーネント
- Server Utils(
- OAuth関連の処理を実装したEvent Handlers(20種類以上のプロバイダーに対応)
- Password関連のUtils ... hash化やverifyメソッド
などなど。認証関連に必要なものが揃っている感じ
とりあえず試してみる
とりあえず、ミニマムな超シンプル版
ディレクトリ構成
こんな感じのディレクトリ構成の例
./ ├── app │ ├── middleware │ │ └── auth.ts │ └── pages │ ├── home.vue │ └── index.vue ├── server │ └── api │ └── auth │ └── google.get.ts └── nuxt.config.ts
ざっくりURLはこんな感じで、3つ用意
http://localhost:3000/
... ログイン画面http://localhost:3000/home
... ログイン後の画面http://localhost:3000/api/auth/google
... Google認証用のAPI
前準備
Google認証に必要なクライアントIDとクライアントシークレットを準備
GCPの「APIとサービス」>「認証情報」> 「OAuth 2.0 クライアントID」から作成
Firebase AuthenticationでGoogle認証を有効してもOK。Firebaseが作ってくれる
クライアントIDを作成したら、以下も設定しておく
反映に少し時間がかかるので注意
- 「承認済みのJavaScript生成元」に「http://localhost:3000」を追加
- 「承認済みのリダイレクトURI」に「http://localhost:3000/auth/google」を追加
nuxt-auth-utilsのインストールと設定
とりあえず、インストール
$ pnpm dlx nuxi@latest init google-auth-sample $ pnpm i -D nuxt-auth-utils
nuxt.config.ts
のmoduleに追加
export default defineNuxtConfig({ compatibilityDate: '2024-11-01', // moduleに追加 modules: ['nuxt-auth-utils'], });
一旦、ここで起動
$ pnpm dev
起動すると、.env
が生成される
# .env NUXT_SESSION_PASSWORD=password-with-at-least-32-characters
ここに、前準備で用意したクライアントIDとクライアントシークレットを追加する
# .env NUXT_SESSION_PASSWORD=password-with-at-least-32-characters + NUXT_OAUTH_GOOGLE_CLIENT_ID=<YOUR_CLIENT_ID> + NUXT_OAUTH_GOOGLE_CLIENT_SECRET=<YOUR_CLIENT_SECRET>
これで準備はOK
APIを作る
次にAPIを作っていく
Google認証用のハンドラーの、
defineOAuthGoogleEventHandler
が用意されてるので、
これだけでOK
// server/api/auth/google.get.ts export default defineOAuthGoogleEventHandler({ // 認証が成功したときの処理 async onSuccess(event, { user }) { // ユーザセッションをCookieに保存 await setUserSession(event, { user }); // ログイン後画面にリダイレクト return sendRedirect(event, '/home'); } });
ログイン画面を作る
ログイン画面はこれだけ。作ったAPIを開くだけ
<!-- app/pages/index.vue --> <template> <div> <div>ログイン画面</div> <a href="/api/auth/google">Googleログイン</a> </div> </template>
※説明用に<a>
を使っているけど、よくない
※<button>
などにするのがいい
認証用のmiddlewareを作る
認証チェックのmiddlewareをシンプル
// app/middleware/auth.ts export default defineNuxtRouteMiddleware(() => { // 用意されたComposablesでログイン状態を取得 const { loggedIn } = useUserSession(); // 未ログインなら、ログイン画面にリダイレクト if (!loggedIn.value) return navigateTo('/'); });
ログイン後画面を作る
ログイン後のホーム画面もシンプルにこんな感じ
認証が必須なので、middleware: 'auth'
を設定
<!-- app/pages/home.vue --> <template> <div> <div>ホーム画面</div> <!-- ログイン情報の確認用 --> <div>user</div> <pre>{{ JSON.stringify(user, null, 2) }}</pre> <div>session</div> <pre>{{ JSON.stringify(session, null, 2) }}</pre> </div> </template> <script setup lang="ts"> definePageMeta({ // 用意したapp/middleware/auth.tsを利用するように設定 middleware: 'auth', }); // 確認用のログイン情報の取得 const { session, user } = useUserSession(); </script>
以上!これでGoogleログインが完成!
あとは、pnpm dev
で起動して、
http://localhost:3000
にアクセスして試せば、ログインできる
超簡単(*´ω`*)
Nuxt Auth Utilsは、何をしてくれているのか?
特にAPI部分のdefineOAuthGoogleEventHandler
が
便利だけど、不思議な感じがする
// server/api/auth/google.get.ts export default defineOAuthGoogleEventHandler({ async onSuccess(event, { user }) { await setUserSession(event, { user }); return sendRedirect(event, '/home'); } });
やってくれていることは、OAuth2.0の認証フローを実行してくれている感じ
この図のNuxtの部分をほぼまるっとしてくれている
Googleのドキュメントだと、このあたり
HTTP/REST
を選ぶと、URLとかが確認できる
user
には何が入っているのか?
実行すると、ホーム画面に表示されるけど、
onSuccess
で渡されるuser
をそのまま設定しているだけ
https://www.googleapis.com/oauth2/v3/userinfo
の返り値をそのまま使っている感じ
なので、OAuthプロバイダごとに、user
の内容が異なるので注意
useUserSession()
のsession
とuser
の違いは?
コンポーザブルのuseUserSession()
があるけど、
実はあまり違いがない
// 確認用のログイン情報の取得 const { session, user } = useUserSession();
ソースコードをみてみると、こんな感じ
// https://github.com/atinux/nuxt-auth-utils/blob/v0.5.7/src/runtime/app/composables/session.ts // ...略 export function useUserSession(): UserSessionComposable { // 取得したユーザセッション const sessionState = useState<UserSession>('nuxt-session', () => ({})); // ...略 return { // ...略 // session、そのまま session: sessionState, // sessionのuserだけ user: computed(() => sessionState.value.user || null), loggedIn: computed(() => Boolean(sessionState.value.user)), // ...略 } }
これを知ったうえで、説明変数(session
)を追加するとこんな感じっぽい
// server/api/auth/google.get.ts export default defineOAuthGoogleEventHandler({ async onSuccess(event, { user }) { const session = { user }; await setUserSession(event, session); return sendRedirect(event, '/home'); } });
その他もろもろ
Session周りはh3のutilを使っている
中を見てみると、h3のuseSession
を使っている感じ
セッション名とか有効期限とか
h3のuseSession
を使っているので、
runtimeConfig
を使って設定する感じ
runtimeConfig: { session: { // default name: 'nuxt-session', password: process.env.NUXT_SESSION_PASSWORD || '', cookie: { sameSite: 'lax' } // 有効期限を設定する場合 // maxAge: 60 * 60 * 24 * 7 // 1 week } }
これもh3のドキュメントを見るといい
onErrorが呼ばれるタイミング
defineOAuthGoogleEventHandler
には、onError
も設定できる
export default defineOAuthGoogleEventHandler({ async onSuccess(event, { user }) {}, onError(event, error) { // エラーが発生したときの処理 } });
どんなものがくるか、ソースを見てみると、こんな感じっぽい
- ①設定が不足している場合
- ②アクセストークン取得でエラーが発生した場合
// https://github.com/atinux/nuxt-auth-utils/blob/v0.5.7/src/runtime/server/lib/oauth/google.ts export function defineOAuthGoogleEventHandler({ config, onSuccess, onError, }: OAuthConfig<OAuthGoogleConfig>) { return eventHandler(async (event: H3Event) => { config = defu(config, useRuntimeConfig(event).oauth?.google, { authorizationURL: 'https://accounts.google.com/o/oauth2/v2/auth', tokenURL: 'https://oauth2.googleapis.com/token', userURL: 'https://www.googleapis.com/oauth2/v3/userinfo', authorizationParams: {}, }) as OAuthGoogleConfig // ...略 // ①設定が不足している場合 if (!config.clientId || !config.clientSecret) { return handleMissingConfiguration(event, 'google', ['clientId', 'clientSecret'], onError) } // ...略 const tokens = await requestAccessToken(config.tokenURL as string, { // ...略 }) // ②アクセストークン取得でエラーが発生した場合 if (tokens.error) { return handleAccessTokenErrorResponse(event, 'google', tokens, onError) } const accessToken = tokens.access_token const user: any = await $fetch( config.userURL as string, // ...略 ) return onSuccess(event, {tokens, user }) }) }
ちゃんとやるときは、userはセットしない
全部、setUserSession()
に設定すると、
見えちゃいけないのもクライアント側に流れてしまうので、
ちゃんと必要なものだけにしておく必要がある
// server/api/auth/google.get.ts export default defineOAuthGoogleEventHandler({ async onSuccess(event, { user }) { const account = await // ... dbからアカウント情報を取得するなどなど await setUserSession(event, { user: account }); return sendRedirect(event, '/home'); } });
NUXT_SESSION_PASSWORDは本番時にも設定しておく
.envが無いときにpnpm dev
を実行すると、
NUXT_SESSION_PASSWORD
してくれる
ただ、本番環境でNUXT_SESSION_PASSWORD
指定なしのままだと、
NUXT_SESSION_PASSWORD
が空のままになるので、注意が必要
# .env NUXT_SESSION_PASSWORD=password-with-at-least-32-characters
User/UserSessionの型を定義する
READMEに書いてあるとおり、型定義ファイルを用意すればOK
// auth.d.ts declare module '#auth-utils' { interface User { // Add your own fields } interface UserSession { // Add your own fields } interface SecureSessionData { // Add your own fields } } export {}
権限周りもいい感じにする
権限周りなどもしたい場合は、nuxt-authorizationがよいらしい
nuxt-auth-utilsと互換性があり、permissionも定義/管理できる
参考にしたサンプル
実際のサンプルなどは、このあたりが参考になった
以上!! 思いの外、簡単にGoogleログインができてしまった(*´ω`*)
参考にしたサイト様
- atinux/nuxt-auth-utils: Add Authentication to Nuxt applications with secured & sealed cookies sessions.
- Nuxt Auth Utils でサーバーサイドの認証を(OAuth・マジックリンク・ワンタイムパスワード)
- Using nuxt-auth-utils to manage API tokens
- Using Nuxt-auth-utils to implement a magic link login
- Minimalist Nuxt Authentication | Vue Mastery
- Full Stack Development with Nuxt | Atinotes
- Nuxt auth utils overview | Restackio
- OAuth 2.0 を使用して Google API にアクセスする | Authorization | Google for Developers