Svelte + TypeScript + firebase authentication でGoogleアカウントログインを実装する。

Svelte + で開発している個人アプリに認証機能が必要になり、導入の容易さ等からfirebase authenticationを導入しました。
その時の手順・やったことを備忘のため残しておきます。

https://firebase.google.com/docs/auth?hl=ja

この記事で作成するアプリのサンプルコードは以下でも公開しています。

https://github.com/tamaki-tech/svelte-typescript-auth-app

## 1. 下準備
実装の前にfirebase側で下準備をします。

`プロジェクトを追加`をクリック。
![スクリーンショット 2022-04-17 13.56.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489509/eb7fdd67-005c-4813-3e65-a8e14229f98c.png)

任意のプロジェクト名を付けます。
![スクリーンショット 2022-04-17 13.56.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489509/b60387db-7a63-c978-3e9e-2f5cff7b6018.png)

これでプロジェクトがされました。`ウェブ`をクリックします。
![スクリーンショット 2022-04-17 13.57.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489509/01e95948-9c0c-0036-74be-9d1e271cf58d.png)

ウェブアプリをプロジェクトに追加します。任意の名称を指定してください。
![スクリーンショット 2022-04-17 13.57.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489509/463c1afa-262b-2630-20f1-d19773548b88.png)

アプリケーションの作成が完了しました。
**このコンフィグは次の手順で使用するのでコピペして保管しておいてください。**
![スクリーンショット 2022-04-17 13.57.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489509/c38d207c-bb53-f6cd-4e81-dd3db3cd698f.png)

次にGoogleアカウントによる認証機能をONにします。`authentication`をクリックしてください。
![スクリーンショット 2022-04-17 13.58.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489509/97f7d0c9-2a5f-03b6-f778-2bb0e479107e.png)

ログイン　プロバイダからGoogleを選択します。
![スクリーンショット 2022-04-17 13.58.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489509/c2a7077b-b337-615d-8c5c-3b3ef7dfc7c1.png)

必須項目を入力して保存してください。
![スクリーンショット 2022-04-17 13.58.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489509/852b5dcc-7688-58da-2961-3ff25e40e8eb.png)

これでfirebase側の設定は完了です。


## 2. Svelte+TypeScriptプロジェクトの作成
以下の手順で公式のテンプレートからプロジェクト作成します。
今回、プロジェクト名は`svelte-typescript-auth-app`とします。
```bash
npx degit sveltejs/template svelte-typescript-auth-app
```

無事にプロジェクトが作成されたら、プロジェクト内に移動してTypeScriptを適用します。
```bash
cd svelte-typescript-auth-app
node scripts/setupTypeScript.js
```

## 3. プロジェクトの下準備

### npmライブラリのインストール

今回は以下のnpmライブラリを使います。

| 名称 | 用途 |
|:-|:-|
| [svelte-routing](https://www.npmjs.com/package/svelte-routing)  |  このアプリケーションでは複数ページを扱う予定ですので、Vue RouterやReact Routerのようなルーティングライブラリを導入します。 |
| [firebase](https://www.npmjs.com/package/firebase)  | firebase公式が提供しているSDKになります。今回はこちらを利用してfirebaseを用いた機能を実装していきます。  |

プロジェクト配下で以下のコマンドでインストールしてください
```bash
npm i svelte-routing firebase
```

### sirvコマンドにオプションを設定

今回はSPAとして実装するので、`package.json`の以下箇所に`--single`オプションを設定してください。
```json:package.json
  "scripts": {
    "build": "rollup -c",
    "dev": "rollup -c -w",
    "start": "sirv public --no-clear --single", // TODO 追加
    "check": "svelte-check --tsconfig ./tsconfig.json"
  },
```

## 4. firebase.tsの作成
下準備で保存しておいた情報でfirebase.tsを作成します。
この設定情報を読み込ませたクラスを作成し、認証で使うクラス群をexportします。

```typescript:src/firebase.ts
import { initializeApp } from "firebase/app";
import { getAuth, GoogleAuthProvider } from "firebase/auth";

const firebaseConfig = {
  // TODO READMEの手順で作成した情報をコピペしてください。
  apiKey: "your api key",
  authDomain: "your auth domain",
  projectId: "your project id",
  storageBucket: "your strage bucket",
  messagingSenderId: "your messaging sender id",
  appId: "your app id"
};

const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
export const provider = new GoogleAuthProvider();

```

## 5. AuthStoreの作成
ログイン情報を保持するStoreクラスを作成します。
```typescript:src/store.ts
import type { UserInfo } from "firebase/auth";
import { writable } from "svelte/store";

export const authStore = writable({ loggedIn: false, user: null as UserInfo });
```

## 6. 画面の作成・ルーティング設定
ログイン画面とマイページ画面(ログイン後に遷移する画面)を作成します。
実際の認証周りの処理はこの後実装していきます。

```typescript:src/Login.svelte
<script lang="ts">
  import { navigate } from "svelte-routing";

  function handleLoginWithGoogle() {
    // TODO TBD
    navigate("/mypage");
  }
</script>

<div>
  <button type="button" on:click={handleLoginWithGoogle}>
    Sign In with Google
  </button>
</div>


```

```typescript:src/Mypage.svelte
<script>
  import { navigate } from "svelte-routing";
  import { authStore } from "./store";

  function handleLogout() {
    // TODO TBD
    navigate("/");
  }
</script>

<h1>Login successed.</h1>
  <ul>
    <li>email:</li>
    <li>name:</li>
  </ul>
  <button type="button" on:click={handleLogout}> Logout </button>
```

ログイン画面とマイページ画面のルーティングを設定します。
```typescript:src/App.svelte
<script lang="ts">
  import { Route, Router } from "svelte-routing";
  import Login from "./Login.svelte";
  import Mypage from "./Mypage.svelte";
</script>

<Router>
  <Route Path="/" component={Login} />
  <Route path="/mypage" component={Mypage} />
</Router>

```

ここまでで画面はこんな感じになっていると思います。

![スクリーンショット 2022-04-19 17.31.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489509/8bfa561e-8a7a-227a-3736-c8cd5ecbfe01.png)
![スクリーンショット 2022-04-19 17.32.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489509/5c24749d-9141-70f8-adcc-37171f9a7c59.png)



## 7. ログイン機能の作成

先に作成した画面にfirebaseによるログイン機能を実装していきます。

まずはログイン画面からやっていきます。
`handleLoginWithGoogle`関数にログイン処理を実装しました。
firebase SDKで公開されている`signInWithPopup`関数に`firebase.ts`でexportした`auth`と`provider`を引数で渡して実行する流れになります。
ログインできたらログイン情報をStoreに保存してMypageへ遷移します。

```typescript:src/Login.svelte
<script lang="ts">
  import { navigate } from "svelte-routing";
  import { signInWithPopup } from "firebase/auth";
  import { auth, provider } from "./firebase";
  import { authStore } from "./store";

  async function handleLoginWithGoogle() {
    await signInWithPopup(auth, provider)
      .then((res) => {
        authStore.set({ ...$authStore, loggedIn: true, user: res.user });
        navigate("mypage");
      })
      .catch((e) => {
        console.log(e);
      });
  }
</script>

<div>
  <button type="button" on:click={handleLoginWithGoogle}>
    Sign In with Google
  </button>
</div>

```

次にMypageにもログアウトの処理・認証済・未済のハンドリング処理を実装します。


```typescript:src/Mypage.svelte
<script>
  import { navigate } from "svelte-routing";
  import { auth } from "./firebase";
  import { authStore } from "./store";

  function handleLogout() {
    auth.signOut();
    navigate("/");
  }
</script>

<h1>Login successed.</h1>
{#if $authStore.loggedIn}
  <ul>
    <li>email: {$authStore.user.email}</li>
    <li>name: {$authStore.user.displayName}</li>
  </ul>
  <button type="button" on:click={handleLogout}> Logout </button>
{/if}



```
## 8. 動作確認
もう一度`npm run dev`コマンドでアプリを再起動し、ログインボタンを押下すると、以下のようなダイアログが表示されてGoogleアカウントでログイン認証ができるはず！
![スクリーンショット 2022-04-19 17.35.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489509/7f6903d6-33de-4f4d-ddfa-4ab440120180.png)



## 8. 参考
- [JavaScript で Google を使用して認証する](https://firebase.google.com/docs/auth/web/google-signin#before_you_begin)