4
5

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.

Firebase v9 + Vue3 + Vite + Vuetify3 beta で firebase auth のメール認証でログインしてみる

Last updated at Posted at 2022-04-24

主旨

Linux/Windows WSL2 (Ubuntu 20.04LTS) でやります。各種ツールのバージョンは以下の通り。

$ npm -v
8.7.0
$ yarn -v
1.22.11
$ vue -V
@vue/cli 5.0.4
$ node -v
v16.14.0
$ firebase --version
10.7.1

準備

vue + vuetify アプリケーションの作成

vue-app の部分は、自分の作りたいプロジェクトの名前に置き換えてください。

$ vue create vue-app
Vue CLI v5.0.4
? Please pick a preset: (Use arrow keys)
❯ Default ([Vue 3] babel, eslint)
  Default ([Vue 2] babel, eslint)
  Manually select features
...
$ cd vue-app
$ vue add vuetify
? Choose a preset:
  Configure (advanced)
  Default (recommended)
❯ Vite Preview (Vuetify 3 + Vite)
  Prototype (rapid development)
  Vuetify 3 Preview (Vuetify 3)
...
$ vue add router
 WARN  There are uncommitted changes in the current repository, it's recommended to commit or stash them first.
? Still proceed? Yes
...
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
...

vscode の拡張機能は volar を使うことが推奨されているようです。

冒頭の vue create vue-app は下記のようにしてもいけます。

$ npm init vite@latest vue-app

typescript にしたい場合は、下記の方法でプロジェクトを作成しないと、うまくいかないようです(vue create ではダメ)。

$ npm init vite@latest vue-app --template vue-ts

Typescript を使った場合で、vue add router したときに Error: Cannot find module '@vue/cli-service/generator/template/src/App.vue' ... というエラーが出た場合は、先に下記コマンドを実行します。

$ npm install --save-dev @vue/cli-service

起動テスト

$ yarn dev

これで http://localhost:3000 にアクセスすると下記の画面が出るはず。

image.png

もし下記のようなエラーが出たら、jsconfig.json の targetes6 にする。

    vite.config.js:5:0:
      5 │ const path = require('path')
        ╵ ~~~~~

  The target environment was set to "es5" here:

    jsconfig.json:3:14:
      3 │     "target": "es5",
        ╵               ~~~~~

このエラーがでたら、

jsconfig.json
{
  "compilerOptions": {
    "target": "es6",
    ...
}

こうします。(vuetify3.0.0 beta 3 からは、このエラーは出なくなった気がします)

router/indes.js で process is not defined のエラーが出る

history: createWebHistory(process.env.BASE_URL),history: createWebHistory(import.meta.env.BASE_URL), に書きかえます。

router/index.js
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes
})

下記参照のこと。

これで動けば、vue まわりの準備は完了です。

firebase 関連の初期化

firebase コンソールでひとつプロジェクトを作って、apiKey を取得しておきます。具体的には下記を参照してください。

firebase init では必要なサービスをカーソルキーとスペースキーで選びます。最低限 hosting は必要です。下記では Hosting (github) 以外すべてを選んでいます。

$ firebase init
...
? Which Firebase features do you want to set up for this directory? Press Space to select features, then Enter to confir
m your choices. (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)
 ◉ Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision default instance
 ◉ Firestore: Configure security rules and indexes files for Firestore
 ◉ Functions: Configure a Cloud Functions directory and its files
❯◉ Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys
 ◯ Hosting: Set up GitHub Action deploys
 ◉ Storage: Configure a security rules file for Cloud Storage
 ◉ Emulators: Set up local emulators for Firebase products
(Move up and down to reveal more choices)

? Please select an option: (Use arrow keys)
❯ Use an existing project
  Create a new project
  Add Firebase to an existing Google Cloud Platform project
  Don't set up a default project

=== Database Setup
? What file should be used for Realtime Database Security Rules? database.rules.json

=== Storage Setup
? What file should be used for Storage Rules? storage.rules

=== Firestore Setup
? What file should be used for Firestore Rules? firestore.rules
? What file should be used for Firestore indexes? firestore.indexes.json

=== Functions Setup
? What language would you like to use to write Cloud Functions? JavaScript
? Do you want to use ESLint to catch probable bugs and enforce style? No
✔  Wrote functions/package.json
✔  Wrote functions/index.js
✔  Wrote functions/.gitignore
? Do you want to install dependencies with npm now? Yes

=== Hosting Setup
? What do you want to use as your public directory? dist
? Configure as a single-page app (rewrite all urls to /index.html)? No
? Set up automatic builds and deploys with GitHub? No
✔  Wrote dist/404.html
✔  Wrote dist/index.html
...

プロジェクトは、firebase のコンソール側で作成したものを選びます。Hostig の public directory は dist にします。

=== Emulators Setup
? Which Firebase emulators do you want to set up? Press Space to select emulators, then Enter to confirm your choices. (
Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)
 ◉ Authentication Emulator
 ◉ Functions Emulator
 ◉ Firestore Emulator
 ◉ Database Emulator
 ◉ Hosting Emulator
 ◉ Pub/Sub Emulator
❯◉ Storage Emulator
? Which port do you want to use for the auth emulator? 9099
? Which port do you want to use for the functions emulator? 5001
? Which port do you want to use for the firestore emulator? 8080
? Which port do you want to use for the database emulator? 9000
? Which port do you want to use for the hosting emulator? 5000
? Which port do you want to use for the pubsub emulator? 8085
? Which port do you want to use for the storage emulator? 9199
? Would you like to enable the Emulator UI? Yes
? Which port do you want to use for the Emulator UI (leave empty to use any available port)?
? Would you like to download the emulators now? No

Emulator は全部チェックしておけばOKです。storage のエミュレータには問題があるようなので、以下のサンプルでは使いませんが、とりあえず有効にだけしておきます。

エミュレータのポート番号はすべてデフォルトで問題ありません。

動作テスト

$ yarn build
$ firebase emulators:start

┌─────────────────────────────────────────────────────────────┐
│ ✔  All emulators ready! It is now safe to connect your app. │
│ i  View Emulator UI at http://127.0.0.1:4000                │
└─────────────────────────────────────────────────────────────┘

┌────────────────┬────────────────┬─────────────────────────────────┐
│ Emulator       │ Host:Port      │ View in Emulator UI             │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Authentication │ 127.0.0.1:9099 │ http://127.0.0.1:4000/auth      │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Functions      │ 127.0.0.1:5001 │ http://127.0.0.1:4000/functions │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Firestore      │ 127.0.0.1:8080 │ http://127.0.0.1:4000/firestore │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Database       │ 127.0.0.1:9000 │ http://127.0.0.1:4000/database  │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Hosting        │ 127.0.0.1:5000 │ n/a                             │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Pub/Sub        │ 127.0.0.1:8085 │ n/a                             │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Storage        │ 127.0.0.1:9199 │ http://127.0.0.1:4000/storage   │
└────────────────┴────────────────┴─────────────────────────────────┘
  Emulator Hub running at 127.0.0.1:4400
  Other reserved ports: 4500

上のような感じになったら、正常に起動しています。http://localhost:5000/ にブラウザでアクセスすると、yarn dev のときと同じ画面が表示されるはずです。

emulator の起動の途中でエラーが出る場合は、

  • firebase init での初期化が足りていない
  • functions 以下のフォルダで npm install の操作がされていない
  • functions の言語を Typescript にした時に npm run build がされていない
  • 他の firebase emulator が動作している(生き残りプロセスがある)

などの理由が考えられます。最後のケースでは ps -aef | grep firebase などとして emulator のプロセスを探して kill します。

firebase の初期化コードを追加する

下記の内容で src/firebase.js というファイルを作ります。

firebase.js
import { initializeApp } from 'firebase/app';
const firebaseConfig = {
    apiKey: "",
    authDomain: "",
    projectId: "",
    storageBucket: "",
    messagingSenderId: "",
    appId: "",
};
const firebase = initializeApp(firebaseConfig);
export const firebaseApp = () => { return firebase };

export default firebase;

firebaseConfig の部分は、firebase のコンソールで取得した apiKey を設定します。

emulator を使用する場合は、さらに下記のようにします (auth, firestore, functions, storage を使用する場合)。

firebase.js
import { initializeApp } from 'firebase/app';
import { getFunctions, connectFunctionsEmulator } from "firebase/functions";
import { getFirestore, connectFirestoreEmulator } from "firebase/firestore";

import { getStorage, connectStorageEmulator } from "firebase/storage";
import { getAuth, connectAuthEmulator } from "firebase/auth";

const firebaseConfig = {
    apiKey: "",
    authDomain: "",
    projectId: "",
    storageBucket: "",
    messagingSenderId: "",
    appId: "",
};
const firebase = initializeApp(firebaseConfig);
const isEmulating = window.location.hostname === "localhost";
if (isEmulating) {

    const auth = getAuth();
    connectAuthEmulator(auth, "http://localhost:9099");

    const storage = getStorage();
    connectStorageEmulator(storage, "localhost", 9199);

    const db = getFirestore(firebase);
    connectFirestoreEmulator(db, 'localhost', 8080); 

    const functions = getFunctions(firebase);
    connectFunctionsEmulator(functions, "localhost", 5001);
}

export const firebaseApp = () => { return firebase };

export default firebase;

firebase.js を .gitignore に追加しておく(オプション)

girhub などに公開状態で置くときは、.gitignore に firebase.js を追加して、firebase.js が公開されないようにしておきます。

.gitignore
.DS_Store
node_modules
/dist

firebase.js

# local env files
.env.local
.env.*.local

# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

firebase の動作テスト

firebase 関連のインストール

最初に以下のコマンドを実行します。

$ yarn add firebase

これは忘れやすい。

firebase のコンソールでメール認証を on にする

下記を参照してください。

image.png

App.vue などの編集

src/App.vue は下記の内容だけにします。

App.vue
<template>
  <v-app>
    <router-view/>
  </v-app>
</template>
<script>
import { firebaseApp } from './firebase';
</script>

src/views/HomeView.vue を以下のように編集します。

HomeView.vue
<template>
    <v-container>
      <div v-if="currentUser == null">
        <v-card width="400px" class="mx-auto mt-5">
          <v-card-actions>
            <v-col>
                <v-text-field 
                    v-model="emailText"
                    label="E-MAIL"
                >
                </v-text-field>
                <v-text-field 
                    v-model="passwordText"
                    label="PASSWORD"
                    type="password"
                >
                </v-text-field>
              <v-btn
                @click="signin"
                color="primary"
              >
                E-Mail SIgn In
              </v-btn>
              <v-btn
                color="primary"
                @click="createAccount"
              >
                Sign Up
              </v-btn>
            </v-col>
          </v-card-actions>
        </v-card>
      </div>
      <div v-else>
        <h2>
          Success to sign in with firebase auth!
        </h2>
        <v-btn @click="signout">
          Sign Out
        </v-btn>
      </div>
  </v-container>    
</template>
<script>
import { getAuth, signOut, onAuthStateChanged, signInWithEmailAndPassword, createUserWithEmailAndPassword } from "firebase/auth";

export default {
    name: 'loginpage',
    data: () => ({
        emailText: "",
        passwordText: "",
        currentUser: null,
    }),
    mounted()
    {
      const auth = getAuth();
      onAuthStateChanged(auth, (user) => {
        if ( user != null ){
          this.currentUser = user;
        }else{
          this.currentUser = null;
        }
      });
    },
    methods: {
        signin()
        {
            if ( this.emailText == "" || this.passwordText == "" ) return;
            const auth = getAuth();
            signInWithEmailAndPassword(auth, this.emailText, this.passwordText)
            .then((userCredential) => {
                // Sign In
                const user = userCredential.user;
                console.log( user );
            })
            .catch((error) => {
                const errorCode = error.code;
                const errorMessage = error.message;
                console.log( errorCode, errorMessage );
            });
        },
        createAccount()
        {
            const auth = getAuth();
            createUserWithEmailAndPassword(auth, this.emailText, this.passwordText)
            .then((userCredential) => {
                // Create Account
                const user = userCredential.user;
                console.log( user );
            })
            .catch((error) => {
                const errorCode = error.code;
                const errorMessage = error.message;
                console.log( errorCode, errorMessage );
            });
        },
        signout()
        {
          const auth = getAuth();
          signOut(auth).then(() => {
            this.currentUser = null;
          }).catch((error) => {
            console.log( error );
          });
        }
    }
}
</script>

動作テスト

$ firebase emulators:start
$ yarn dev

上のコマンドは、別々のターミナルで実行する必要があります。これで http://localhost:3000 をブラウザで開くと、下記のような画面が出ます。

image.png

email と password を入力して SIGN UP を押すと、アカウントが作成されてログインした状態になります。

image.png

たとえばこんな感じです。

ログインした状態になると、下記の画面になります。

image.png

こ画面で、SIGN OUT を押すと、ログアウトできます。

次にログインするときは、メールアドレスとパスワードを入れて E-MAIL SIGN IN を押します。

この状態で http://localhost:4000/auth をブラウザで開くと、エミュレータ上にアカウントが作成されているのが確認できます。

image.png

Deploy

$ yarn build
$ firebase deploy

いろいろ

全部のページで firebase を使うなら、src/firebase.js を書いた上で App.vue で下記のように書いておくと、個別のページで import しなくてよくなります。

App.vue
<script>
import { firebaseApp } from './firebase';
</script>

一部のページだけで使うなら、都度 import したほうがよい、のかなあ?

ログインした後、別のページに飛ばしたければ、

.js
this.$router.push('/home');

などとして飛ばします。

storage のエミュレータで、サイズが大きなファイルを扱おうとすると、エミュレータが落ちることがあるようです。storage はオンライン側を使ったほうがいいかもしれません。その場合は、firebase.js のエミュレータ関連の部分は下記のようにコメントアウトします。

firebase.js
const firebase = initializeApp(firebaseConfig);
const isEmulating = window.location.hostname === "localhost";
if (isEmulating) {

    // const auth = getAuth();
    // connectAuthEmulator(auth, "http://localhost:9099");

    //const storage = getStorage();
    //connectStorageEmulator(storage, "localhost", 9199);

    const db = getFirestore(firebase);
    connectFirestoreEmulator(db, 'localhost', 8080); 

    const functions = getFunctions(firebase);
    connectFunctionsEmulator(functions, "localhost", 5001);
}

storage をオンライン側で使う場合、auth もオンライン側を使用しないと、認証時に失敗します。

おまけ

Windows で WSL を使っている場合で、レポジトリのファイルが Windows File System ↑にあると、HMR (オートリロード)が実行されません。

vite.config.js に下記を追加すると、機能が有効になります。

vite.config.js
  server: {
    watch: {
      usePolling: true
    }
  },
4
5
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
4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?