0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Nuxt 3 + Google OAuth (PKCE) を SPA(CSR)構成で動かす最小構成ガイド

Posted at

✍ 本記事は AI(ChatGPT)を活用して下書きを作成し、筆者が検証・編集のうえ公開しています。

TL;DR

  • Google OAuth + PKCE を Nuxt3 CSR で使うと client_secret 問題に詰まる
  • ミニ BFF (Express / Spring でも可) に /auth/exchange を置けば解決
  • フルサンプルは GitHub → nuxt3-google-oauth-pkce-sample

背景

Google の OAuth PKCE フローは Web アプリ登録だと client_secret が必須。CSR 環境で動かすにはBFFでトークン交換を代行させるのが現実的な解法です。この記事では Nuxt 3 CSR + Express マイクロサービス構成のサンプルをステップごとに紹介します。
あるプロジェクトで Nuxt 3 CSR アプリとバックエンド API を組み合わせる際、Google の PKCE フローで client_secret が必須となり動作しない問題が発生しました。本記事では、それを解決するための Express マイクロサービス(BFF)的な構成を解説します。

目次

  1. 背景 ― なぜシークレット問題にハマるのか

  2. SPA(CSR)構成 で取り得る 4 つのアーキテクチャ

  3. 実装ステップ

  4. Google Cloud 側の設定

  5. Express マイクロサービス (コード → トークン交換)

  6. Nuxt 3 CSR クライアント

  7. 付録 A. デプロイ & サーバーレス化 Tips

  8. まとめ・今後の改善ポイント

1. 背景 ― Google PKCE で client_secret が必須になる理由

  • RFC 7636 (PKCE) は本来 Public Client 向け → シークレット不要のはず

  • Google は Web アプリ 登録時に「クライアント認証は必須」という実装ポリシー

  • CSR 単体ではトークンエンドポイントを呼べず、BFF などでシークレットを隠蔽する必要がある

2. フロント単体 (CSR) で取り得る 4 つのアーキテクチャ

パターン 概要 Pros Cons
A. CSR + マイクロサービス (今回) Express/Lambda がトークン交換 Google 対応可静的ホスティングと親和性◎ サーバー管理が必要
B. SSR + BFF Nuxt SSR 内でシークレット保持 同一オリジンで完結 SSR 運用コスト
C. Public Client プロバイダ Auth0 / Azure AD など フロントだけで完結 Google 利用不可
D. Implicit Flow レガシー方式 サーバー不要 セキュリティ弱い

本記事では A: CSR + ミニ Express (Spring でも可) を実装。

2.1 他の実装パス

  • Spring Bootに spring-boot-starter-oauth2-client を組み込む
    → バックエンドが Java の場合はコード→トークン交換を Spring Security が代行できる
    特定プロバイダのライブラリに依存するため今回は不採用

3. 実装ステップ

3.1 Google Cloud 設定

  1. OAuth 同意画面: アプリ名・ドメイン登録

  2. 認証情報 → OAuth 2.0 クライアント ID

  3. client_id と client_secret を控える

3.2 Express BFF (server/)

  • 依存: express cors dotenv node-fetch qs
  • 重要ポイント
    • express.urlencoded() で form-urlencoded をパース
    • /auth/exchangeclient_secret を付けてトークン取得
router.post('/', async (req, res, next) => {
  const { code, code_verifier } = req.body
  const conf = await fetch(process.env.OIDC_WELL_KNOWN).then(r=>r.json())
  const token = await fetch(conf.token_endpoint, {
    method:  'POST',
    headers: {'Content-Type':'application/x-www-form-urlencoded'},
    body: qs.stringify({
      grant_type:'authorization_code',
      client_id:process.env.OIDC_CLIENT_ID,
      client_secret:process.env.OIDC_CLIENT_SECRET,
      redirect_uri:process.env.OIDC_REDIRECT_URI,
      code, code_verifier
    })
  }).then(r=>r.json())
  res.json(token)
})

3.3 Nuxt 3 CSR (client/)

  • 依存: @pinia/nuxt oidc-client-ts

  • plugins/oidc.client.js で token_endpoint を apiBase + /auth/exchange に上書き

  • handleCallback()signinCallback() → トークンとプロフィールをストアへ

export const handleCallback = async () => {
  const user = await mgr.signinCallback()
  store.setTokens({ access_token: user.access_token })
  store.setUser(user.profile)
  await navigateTo('/')
}

3.4 動作確認

# server
npm run dev
# client
npm run dev

付録 A. デプロイ & サーバーレス化 Tips

レイヤー ポイント
フロント Netlify / Vercel / Cloudflare Pages nuxt generate 出力で即デプロイ
BFF Vercel Functions / Cloudflare Workers / AWS Lambda .env シークレットはプラットフォーム管理

4. まとめ・今後の改善

  • リフレッシュトークン運用:httpOnly Cookie + Token Rotation

  • セキュリティ強化express-rate-limit & Helmet & CORS 制限

  • 別 ID プロバイダ:Microsoft identity platform 等ならフロント完結も可能

  • SSR/BFF 移行:必要に応じて Nuxt Nitro に統合するとオリジンを一本化できる

5. ソースコード

GitHub にて今回紹介した構成の全コードを公開しています:
nuxt3-google-oauth-pkce-sample - GitHub リポジトリ
静的ホスティング × Google OAuth の“微妙な壁”にハマった方の助けになれば幸いです!

0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?