23
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Vue 3+TypeScript+Vite】Chrome拡張機能(MV3) + FirebaseでGoogle認証する

Last updated at Posted at 2021-11-28

経緯

Chrome拡張機能のMV2でFirebaseを使ってGoogle認証するという記事は見つかるのですが、2021/11/27時点でMV3で作られている記事はほとんどなくハマりポイントが多かったので整理も兼ねて記事にしてみようと思います。

前半(Docker + Vueでビルド環境を作る)は主に開発環境構築の話となります。
Chrome拡張機能(MV3) + FirebaseでGoogle認証するに関する部分は後半になります。こちらから後半部に遷移できます。

リポジトリ

今回の記事で使ったコードは下記リポジトリにあげているので参考にしてください。
https://github.com/snyt45/chrome-extensions-mv3-google-authenticator-with-firebase

Docker + Vueでビルド環境を作る

Chrome拡張機能はHTML、CSS、JSで書けるのですが今回は勉強も兼ねてVueを使ってChrome拡張機能に必要なファイルを生成するためのビルド環境を構築していきます。

今回の構成

  • Docker
    • docker-composeを使います。
  • Node.js
    • JavaScriptの実行環境
  • npm
    • Node Package Manager.Node.jsにデフォルトで付属しているコマンドでNode.jsのパッケージ管理をするためのツール。
  • npx
    • npm v5.2.0からnpmに同梱されるようになったコマンドでNode.jsのパッケージを実行するためのツール。
    • 本記事では登場しませんが、実際に動作確認する際にnpxは便利なので結構使っています。
  • Vue3 + TypeScript
    • JavaScriptフレームワークのVue3とAltJS(コンパイルすることで最終的にJavaScriptのコードを生成する)の一つのTypeScriptを使います。
  • Vite
    • Vue.jsのプロジェクト作成や実行ができるツール

Docker上に環境構築

今回はNode.jsがインストールされているDockerイメージをベースに構築していきます。

ViteにはNode.jsのバージョンが12以上必要みたいなので、今回はNode.jsのバージョン14のイメージを使います。

docker-compose.yml
version: '3'

services:
  app:
    container_name: vue-app-container
    image: node:14
    working_dir: /app
    ports:
      - 3000:3000
    volumes:
      - .:/app
    stdin_open: true

stdin_open: trueは、stdin_open: trueがないとdocker-compose up後にコンテナが終了してしまうのでコンテナを起動し続けるために必要でした。

docker-compose.ymlを作成したら、コンテナのビルド&起動を行います。

docker-compose up -d

コンテナが起動したら、コンテナ内でbashを対話シェルとして実行します。

docker-compose exec app /bin/bash

コンテナはNode.jsがすでにインストールされている環境なので、node npm npxのコマンドが使えることを確認します。

root@64985558fba6:/app# node -v
v14.18.1
root@64985558fba6:/app# npm -v
6.14.15
root@64985558fba6:/app# npx -v
6.14.15

Viteでプロジェクトを生成する

Viteでプロジェクトを生成するコマンドを実行します。

npm init vite@latest

コマンドライン上で対話形式で入力していくと、プロジェクトが生成されます。
(3つ入力または選択するだけでプロジェクトが生成されました…ほんとに高速で驚きです!)

root@64985558fba6:/app# npm init vite@latest
npx: installed 6 in 3.059s
✔ Project name: … sample-project
✔ Select a framework: › vue
✔ Select a variant: › vue-ts

Scaffolding project in /app/sample-project...

Done. Now run:

  cd sample-project
  npm install
  npm run dev

app/sample-projectディレクトリに生成されたファイルをappディレクトリ配下に展開するために下記コマンドを実行します。

# sample-projectに移動
cd sample-project

# sample-project内の全てのファイルを一階層上(app/)に移動
mv * ../

# sample-projectディレクトリは不要になるので削除
cd ../
rm -rf sample-project

サーバーを起動して、ブラウザーで「http://localhost:3000/」にアクセスします。

# package.jsonのライブラリをインストール
npm install

# 開発用のWebサーバー起動
npm run dev

ですが、Vueの初期ページがされず..

下記を参考にvite.config.jsに設定を追加したところ、正常に表示されました。

dockerにviteを入れてvueで開発しようとしたときlocalhostに繋がらなかった話 | SEとDIY

vite.config.js
export default defineConfig({
  plugins: [vue()],
+  server: {
+    host: true
+  }
})

正常に表示されている画像。

image.png

動作確認できたので、今回不要なファイルを削除しておきます。

# index.html削除
rm -rf index.html

# public削除
rm -rf public

# src配下のファイルを削除
cd src
rm -rf *

Chrome拡張機能用のファイルを生成できるようにする

今回Vueを使いますが最終的にはHTMLやJSなどをextension/フォルダにビルドし、そのファイルをChrome拡張用のファイルとして使います。

Chrome拡張機能用のビルド後のディレクトリ構成

ビルドを実行した際に下記のようなディレクトリ構成が作成されるようにします。

extension
|- dist/
|  |- assets/         # index.htmlから読み込むjsなどが配置される
|  |- popup/          # popup用のUIファイル
|      |- index.html  
|- manifest.json      # Chrome拡張機能のエントリポイント

manifest.jsonを生成するためのビルド環境を作る

Chrome拡張機能のエントリポイントであるmanifest.jsonをextension/にビルドできるようにしていきます。

extensionディレクトリを作成しておきます。

mkdir extension

先に必要になるライブラリをインストールしておきます。

npm install --save-dev @types/node # pathモジュールの型情報を取得するため
npm install --save-dev @types/fs-extra # fs-extraモジュールの型情報を取得するため
npm install --save-dev fs-extra # fs-extraモジュールを使えるようにするため
npm install --save-dev esno # manifest.tsをバッチのように実行するビルドツール

manifest.jsonを作成するためのmanifets.tsを作成します。
(よく使うメソッドはscripts/utils.tsに作成しておきます。)

scripts/utils.ts
import { resolve } from 'path'

export const r = (...args: string[]) => resolve(__dirname, '..', ...args)
src/manifest.ts
import fs from 'fs-extra'
import { r } from '../scripts/utils'

const getManifest = () => {
  return {
    manifest_version: 3,
    name: 'Getting Started Example',
    description: 'Build an Extension!',
    version: '1.0',
    action: {
      default_popup: './dist/popup/index.html'
    }
  }
}

function writeManifest() {
  fs.writeJSON(r('extension/manifest.json'), getManifest(), { spaces: 2 })
}

writeManifest()

package.jsonにmanifest.jsonを生成するnpmスクリプトを追加します。

package.json
  "scripts": {
    "build:manifest": "esno src/manifest.ts"
  },

npm run build:manifestを実行してextensionディレクトリにmanifest.jsonが作られれば成功です。

popup用のUIファイルを生成するためのビルド環境を作る

src配下にpopupフォルダを作成します。

mkdir src/popup

Viteではindex.htmlがエントリポイントになります。
エントリポイントとなるファイルを作成します。
ビルド時にエントリポイントで読み込まれているmain.tsもバンドルされます。

src/popup/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Popup</title>
</head>
<body style="min-width: 100px">
  <div id="app"></div>
  <script type="module" src="./main.ts"></script>
</body>
</html>

エントリポイントから読み込むファイルも作成します。

src/popup/main.ts
import { createApp } from 'vue'
import App from './Popup.vue'

const app = createApp(App)
app.mount('#app')
src/popup/Popup.vue
<template>
  <div>
    Hello World!!
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
  name: 'Popup',
  setup() {
    return {}
  },
})
</script>

このままだとvueファイルを認識できていなかったため、env.d.tsを追加しました。
(src配下を一度削除しましたが、そのときに必要なenv.d.tsも削除してしまっていたようです…)

src/env.d.ts
/// <reference types="vite/client" />

declare module '*.vue' {
  import { DefineComponent } from 'vue'
  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
  const component: DefineComponent<{}, {}, any>
  export default component
}

popup用のUIファイルをビルドするためにvite.config.tsに設定を追加します。

vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { r } from './scripts/utils'

// https://vitejs.dev/config/
export default defineConfig({
  root: r('src'),
  build: {
    outDir: r('extension/dist'),
    emptyOutDir: false,
    rollupOptions: {
      input: {
        popup: r('src/popup/index.html'),
      },
    },
  },
  plugins: [
    vue(),
    // rewrite assets to use relative path
    {
      name: 'assets-rewrite',
      enforce: 'post',
      apply: 'build',
      transformIndexHtml(html) {
        return html.replace(/"\/assets\//g, '"../assets/')
      },
    },
  ],
  server: {
    host: true
  }
})

package.jsonにextensionディレクトリにdistディレクトリを生成するnpmスクリプトを追加します。

package.json
  "scripts": {
    "build:manifest": "esno src/manifest.ts",
+    "build:popup": "vite build"
  },

npm run build:popupを実行して、extensionディレクトリにdistディレクトリが生成されて中にassetsやpopupディレクトリが生成されていれば成功です。

ビルドスクリプトを微調整

まずは必要になるライブラリをインストールします。

npm install --save-dev rimraf # ファイル・ディレクトリ削除用のライブラリ
npm install --save-dev npm-run-all # npmスクリプトをシーケンシャル(ひとつずつ順番に)実行できるライブラリ

npmスクリプトを修正します。
buildコマンドとclearコマンドが新たにできたのでビルドするとき便利になりました。

package.json
  "scripts": {
+    "build": "run-s clear build:manifest build:popup",
    "build:manifest": "esno src/manifest.ts",
    "build:popup": "vite build",
+    "clear": "rimraf extension/dist extension/manifest.json"
  },

ビルドがうまくいったか動作確認

ここまででChrome拡張機能に最低限必要なファイルがビルドできるようになったので、実際に確認します。

npm run build

chrome://extensions/ にアクセスします。

左上の「パッケージ化されていない拡張機能を読み込む」から生成したextensionディレクトリを指定します。

Snipaste_2021-11-27_23-09-05.png

追加に成功すると次のように表示されます。

image.png

ブラウザ右上のChrome拡張機能をクリックしてHello Worldが表示されれば成功です!

Snipaste_2021-11-27_23-12-42.png

Chrome拡張機能(MtV3) + FirebaseでGoogle認証する

ビルド環境ができたので、やっと本題になります。

最終的には**chrome.identity.getAuthToken+signInWithCredentialを使う方法でChrome拡張機能(MV3) + FirebaseでGoogle認証することができました。**

chrome.identity.getAuthTokenとは

chromeで使えるAPI。OAuth2アクセストークンを取得します。
https://developer.chrome.com/docs/extensions/reference/identity/

signInWithCredentialとは

Firebaseで使えるAPI。指定された資格情報を使用して非同期にサインインします。
https://firebase.google.com/docs/reference/js/auth.md#signinwithcredential

Chrome拡張機能(MtV3) + FirebaseでGoogle認証するまでの流れ

1. Firebaseのプロジェクトを作成する

Firebaseのログインを済ませ、FirebaseコンソールのFirebaseプロジェクトから「プロジェクトを追加」します。

Snipaste_2021-11-28_19-43-14.png

プロジェクト名を入力します。ここでは、「sample-project」にしています。

Snipaste_2021-11-28_19-43-43.png

Googleアナリティクスは今回必要ないので無効にします。

Snipaste_2021-11-28_19-44-14.png

少し待つとプロジェクトが作成されます。

Snipaste_2021-11-28_19-44-54.png

続行を押して、FIrebaseのプロジェクトに移動して次のような画面が表示されればプロジェクトが作成できています。

Snipaste_2021-11-28_19-48-16.png

2. Firebaseのマイアプリを登録する。

Chrome拡張機能からGoogle認証するうえでマイアプリが必要になるので、「プロジェクトの設定」から登録します。

Snipaste_2021-11-28_20-02-40.png

マイアプリの赤枠アイコンをクリックして登録していきます。

Snipaste_2021-11-28_20-05-55.png

アプリのニックネームを入力します。

Snipaste_2021-11-28_20-07-09.png

FirebaseのSDKの追加を行います。
今回は、「npmを使用する」にチェックします。

この手順に従って、SDKをインストールしておきます。
(SDKをインストールしておくことで、Chrome拡張機能プロジェクト内にFirebaseスクリプトを含むことになるので、エラーにならずに使用することができます。)

npm install --save-dev firebase # Firebase SDKのインストール

「コンソールに進む」ことでマイアプリが登録されます。

Snipaste_2021-11-28_20-09-53.png

ひとまず、ここでFirebaseの初期化までエラーなく動くか試しておきます。

src/popup/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Popup</title>
</head>
<body style="min-width: 100px">
  <div id="app"></div>
  <script type="module" src="./main.ts"></script>
+  <script type="module" src="./firebase.ts"></script>
</body>
</html>
src/popup/firebase.ts
import { initializeApp } from 'firebase/app'

const firebaseConfig = {
  apiKey: import.meta.env.VITE_FIREBASE_APIKEY,
  authDomain: import.meta.env.VITE_FIREBASE_AUTHDOMAIN,
  projectId: import.meta.env.VITE_FIREBASE_PROJECTID,
  storageBucket: import.meta.env.VITE_FIREBASE_STORAGEBUCKET,
  messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGINGSENDERID,
  appId: import.meta.env.VITE_FIREBASE_APPID
}
const app = initializeApp(firebaseConfig)
console.log(app)

環境変数は.env.localを作ってそちらから読み込むようにしました。
(.gitignoreでgit管理対象外にしておきましょう)

.env.local
# Firebase configuration
VITE_FIREBASE_APIKEY=XXXXXXXXXXX
VITE_FIREBASE_AUTHDOMAIN=XXXXXXXXXXX
VITE_FIREBASE_PROJECTID=XXXXXXXXXXX
VITE_FIREBASE_STORAGEBUCKET=XXXXXXXXXXX
VITE_FIREBASE_MESSAGINGSENDERID=XXXXXXXXXXX
VITE_FIREBASE_APPID=XXXXXXXXXXX

npm run buildして、Chrome拡張機能を再読み込みします。

ブラウザ右上の拡張機能のアイコンを右クリックして「ポップアップ検証」からコンソールを開きます。

Snipaste_2021-11-28_20-33-31.png

エラーが出ていなければここまでの動作確認は成功です。

Snipaste_2021-11-28_20-33-54.png

3. Google Cloud Platform(GCP)のプロジェクトの確認

ここが重要なのですが、Firebaseでプロジェクトを作成した時点でGoogle Cloud Platform(GCP)側で同じプロジェクト名でプロジェクトが作成されています。

ここでは、sample-projectをクリックしてGCPのプロジェクトのダッシュボードに移動します。

Snipaste_2021-11-28_19-47-56.png

プロジェクトのダッシュボードの左メニューにある「APIとサービス」の認証情報をクリックします。

Snipaste_2021-11-28_19-52-37.png

「APIキー」、「OAuth 2.0 クライアント ID」がFirebaseによって自動で作成されていることが分かります。

Snipaste_2021-11-28_19-57-45.png

後述のハマりポイント③にも書いていますが、自動で作成されている「OAuth 2.0 クライアント ID」を削除するとFIrebase認証でGoogleを追加する際にエラーが発生するので残しておきます。

4. chrome.identity.getAuthTokenを使うための準備

こちらを参考にして準備していきます。
https://developer.chrome.com/docs/apps/app_identity/

4.1 マニフェストに権限を追加します。
src/manifest.ts
const getManifest = () => {
  return {
    manifest_version: 3,
    name: 'Getting Started Example',
    description: 'Build an Extension!',
    version: '1.0',
    action: {
      default_popup: './dist/popup/index.html'
    },
+    permissions: [
+      'identity'
+    ],
  }
}

4.2 OAuth2クライアントIDを取得する

すでにFirebaseが自動でOAuth 2.0 クライアント IDを作成していますが、種類が「ウェブ アプリケーション」になってしまっているため新たに作成していきます。

その前に、OAuth同意画面の設定を行います。

User Typeを「外部」にチェックして、作成します。

Snipaste_2021-11-28_21-27-53.png

アプリ名、ユーザーサポートメール、デベロッパーの連絡先情報を入力して保存して次へ。

Snipaste_2021-11-28_21-28-57.png

Snipaste_2021-11-28_21-29-38.png

スコープは「Googleアカウントのメールアドレスの参照」にチェックを入れて保存して次へ。

Snipaste_2021-11-28_21-30-03.png

省略可能な情報は、何もせず保存して次へ。

再度、OAuth 同意画面に戻りテストユーザーに自分のGoogleアカウントのメールアドレスを登録しておきます。

Snipaste_2021-11-28_21-33-55.png

続いて、OAuthクライアントIDを作成していきます。

Snipaste_2021-11-28_21-35-04.png

アプリケーションの種類を「Chromeアプリ」、名前を「sample-project」、アプリケーションIDはChrome拡張機能の画面から取得したIDを入力して作成します。

Snipaste_2021-11-28_21-36-06.png

アプリケーションIDは下記から取得します。

Snipaste_2021-11-28_21-35-54.png

これでOAuth2クライアントIDを取得できるようになりました。

Snipaste_2021-11-28_21-38-48.png

4.3 chrome.identity.getAuthTokenでアクセストークンを取得

アクセストークンを取得できる準備ができたので実装をしていきます。

まずは、chromeのAPIを使う際に必要なライブラリをインストールしておきます。

npm install --save-dev @types/chrome # chromeの型情報を取得するため

マニフェストに取得したOAuth2クライアントIDとスコープを追加します。

src/manifest.ts
const getManifest = () => {
  return {
    manifest_version: 3,
    name: 'Getting Started Example',
    description: 'Build an Extension!',
    version: '1.0',
    action: {
      default_popup: './dist/popup/index.html'
    },
    permissions: [
      'identity'
    ],
+    oauth2: {
+      client_id: 'XXXXXXXXXXXX.apps.googleusercontent.com',
+      scopes: ['https://www.googleapis.com/auth/userinfo.email']
    }
  }
}

アクセストークンを取得するための処理を追加します。

src/manifest.ts
import { initializeApp } from 'firebase/app'

const firebaseConfig = {
  apiKey: import.meta.env.VITE_FIREBASE_APIKEY,
  authDomain: import.meta.env.VITE_FIREBASE_AUTHDOMAIN,
  projectId: import.meta.env.VITE_FIREBASE_PROJECTID,
  storageBucket: import.meta.env.VITE_FIREBASE_STORAGEBUCKET,
  messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGINGSENDERID,
  appId: import.meta.env.VITE_FIREBASE_APPID
}
initializeApp(firebaseConfig)

// ChromeアプリからGoogleログインしてトークン取得
chrome.identity.getAuthToken(
  {interactive: true},
  (token: string) => {
    console.log('token', token)
  }
)

npm run buildを行い、Chrome拡張機能を再読み込みしてChrome拡張機能のアイコンをクリックすると、Googleのサインインのポップアップが表示されるので、ログインするユーザーを選択します。

Snipaste_2021-11-28_21-40-12 (2).png

「Continue」を選択します。

Snipaste_2021-11-28_21-40-30 (2).png

「Allow」を選択します。

Snipaste_2021-11-28_21-40-48 (2).png

コンソールを開いて、ログインユーザーのtokenが表示されていれば成功です。

Snipaste_2021-11-28_21-45-28.png

5. FirebaseでGoogle認証するための準備

5.1 AuthenticationでGoogle認証を追加する

Authenticationで「始める」をクリックします。

Snipaste_2021-11-28_22-09-43.png

Sign-in methodの中から「Google」を選択します。

Snipaste_2021-11-28_22-10-56.png

「有効にする」を選択し、プロジェクトの公開名、プロジェクトのサポートメールを入力・選択して保存します。

Snipaste_2021-11-28_22-13-42.png

Googleが有効になっていればOKです。

Snipaste_2021-11-28_22-57-47.png

5.2 signInWithCredentialを使ってFirebaseでGoogle認証する

firebase.tsを下記のように修正します。

src/popup/firebase.ts
import { initializeApp } from 'firebase/app'
import { getAuth, GoogleAuthProvider, signInWithCredential } from 'firebase/auth'

const firebaseConfig = {
  apiKey: import.meta.env.VITE_FIREBASE_APIKEY,
  authDomain: import.meta.env.VITE_FIREBASE_AUTHDOMAIN,
  projectId: import.meta.env.VITE_FIREBASE_PROJECTID,
  storageBucket: import.meta.env.VITE_FIREBASE_STORAGEBUCKET,
  messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGINGSENDERID,
  appId: import.meta.env.VITE_FIREBASE_APPID
}
initializeApp(firebaseConfig)
const auth = getAuth()

// ChromeアプリからGoogleログインしてトークン取得
chrome.identity.getAuthToken(
  {interactive: true},
  (token: string) => {
    console.log('token', token)
    // Googleログイン成功時に受け取るトークンを使ってGoogleのクレデンシャル作成
    const credential = GoogleAuthProvider.credential(null, token)
    console.log('credential:', credential)
    console.log('auth:', auth)

    // Googleユーザーのクレデンシャルを使ってサインイン
    signInWithCredential(auth, credential).then((result) => {
      console.log("Sign In Success", result)
    }).catch((error) => {
      console.log("Sign In Error", error)
    })
  }
)

いつも通りnpm run buildを行って、Chrome拡張機能を再読み込みしてアイコンをクリックしてコンソールを開きます。

下記のようにSign In SuccessとなっていればFirebaseでGoogle認証できています。

Snipaste_2021-11-28_23-00-26.png

ここまで長かったですね!!お疲れ様でした!
最後にChrome拡張機能(MV3)とFirebaseでGoogle認証のハマりポイントを紹介していきます。

Chrome拡張機能(MV3) + FirebaseでGoogle認証する際のハマりポイント

ハマりポイント① MV3からはリモートでホストされるコードは実行できなくなります

これがMV3で作成する際の一番のハマりポイントだと思います。

Chrome拡張機能の開発について検索する際にまだまだMV2で作成したときの記事が多く、APIを叩くような拡張機能を作る際には記事通りに作っているので動かないということが多かったです。

下記はGoogle公式サイトからの引用です。

リモートでホストされるコード
**MV3 では、拡張機能が JavaScript や Wasm ファイルなどのリモート コードを読み込めないようになったことが、セキュリティ上の重要な改善点です。**これにより、Chrome ウェブストアに送信される拡張機能の安全な動作を、より確実かつ効率的に審査できるようになりました。具体的には、すべてのロジックが拡張機能のパッケージに含まれている必要があります。
https://developer.chrome.com/docs/extensions/mv3/intro/mv3-overview/#remotely-hosted-code

リモートでホストされるコードとは具体的には下記を指すようです。

リモートでホストされるコードとは、拡張機能のパッケージにロード可能なリソースとして含まれていないコードを指します。たとえば、次の両方がリモートでホストされているコードと見なされます。

こちらについては、Chrome拡張機能に必要なスクリプトは拡張機能に含めることで回避できます。

MV2のサポートタイムラインはこちらにあり、段階的にMV3に移行していくようですが、2023年6月には全てのMV2のChrome拡張機能が使えなくなるようです。

ハマりポイント② firebaseの例でよく紹介されているsignInWithPopup自体はローカルからメソッド呼び出しできるがさらにそこからGoogleのリモートコードを実行するため使えない

Firebaseでの認証にGoogle認証を使う方法があるのですが、その中でsignInWithPopupを使う方法が紹介されています。

私の環境で動作するコードに書き換えていますが、下記のようなイメージです。

import { initializeApp } from 'firebase/app'
import { getAuth, GoogleAuthProvider, signInWithPopup } from 'firebase/auth'

const firebaseConfig = {
  apiKey: import.meta.env.VITE_FIREBASE_APIKEY,
  authDomain: import.meta.env.VITE_FIREBASE_AUTHDOMAIN,
  projectId: import.meta.env.VITE_FIREBASE_PROJECTID,
  storageBucket: import.meta.env.VITE_FIREBASE_STORAGEBUCKET,
  messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGINGSENDERID,
  appId: import.meta.env.VITE_FIREBASE_APPID
}
initializeApp(firebaseConfig)
const auth = getAuth()
const provider = new GoogleAuthProvider()

signInWithPopup(auth, provider)
  .then((result) => {
    console.log(result)
  }).catch((error) => {
    console.log(error)
  })

こちらを実行するとコンソールに下記エラーが表示されます。
Content Security Policy違反しているよ。外部のスクリプトを実行する場合はCSPヘッダに追加してねという感じです。

Refused to load the script 'https://apis.google.com/js/api.js?onload=__iframefcb717050' because it violates the following Content Security Policy directive: "script-src 'self'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.

manifest.jsonに下記のようにhttps://apis.google.com/js/api.jsを許可リストに含めてみます。

manifest.json
# NG
  "content_security_policy": {
    "extension_pages": "script-src 'self' https://apis.google.com/js/api.js; object-src 'self'"
  },

すると、下記のように読み込み失敗するようになってしまいました。

image.png

manifest.jsonの書き方が間違っているのか確かめるために、https://apis.google.com/js/api.jsを削除した状態だと読み込むことができました。

manifest.json
# OK
  "content_security_policy": {
    "extension_pages": "script-src 'self'; object-src 'self'"
  },

CSPの読み込みが失敗する原因まではわかりませんでしたが、基本的にはリモートスクリプトはダメという方針なので、そもそもCSPで許可してあげても実行できないので意味がないです。

これらのことから分かるように、signInWithPopupは内部でhttps://apis.google.com/js/api.jsのスクリプトを呼び出しているため、MV3では使うことができません。

最終的には下記の記事を参考にchrome.identity.getAuthToken+signInWithCredentialを使う方法で回避しました。

Chrome拡張+FirebaseでGoogle認証するいくつかの方法 – rinoguchiブログ

ハマりポイント③ FirebaseのAuthenticationでGoogleを有効にし用とした際に「Google の更新中にエラーが発生しました」と表示されて保存できない

この問題は、Firebaseによってデフォルトで自動生成されたOAuthクライアントを削除することで発生するようです。

私は諦めて、再度Firebaseのプロジェクトを作成するところからやり直すことで解決しました…

参考記事紹介

まとめ

今回はビルド環境にViteを使ってみたり、初めてのChrome拡張機能開発で調査がしんどいうえにFirebaseのGoogle認証までやったのでハマりどころが多すぎてやりたいことができずに辛かったです…

今後、MV3でChrome拡張機能される方の少しでも参考になれば記事を頑張って書いてよかったなと思えるのでLGTM頂けると嬉しいですmm

現場からは以上です。

23
10
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
23
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?