くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

SPAなNuxtアプリでFirebase Authのredirectでも、認証後に来たベージに戻りたい(Firebase UI)

開発している積読ハウマッチで、Googleアカウントでのログインに対応した際、
Firebase Authの認証フローをポップアップからリダイレクトに変えてみた。

ポップアップだと、どこでもログインできたけど、
ログイン画面だと元のページに戻りたく、いろいろ考えたときの備忘録。

こんな感じ

どのページから来たかを覚えておけばいいかなと思ったので、

  1. beforeRouteEnterで、前のページのパスを取得
  2. クエリパラメタに付与しておく
  3. 認証後にクエリパラメタがあったら、そのパスに遷移

という感じにしてみた。

まずはパッケージのインストール

$ npm i firebase firebaseui firebaseui-ja

ソースはこんな感じ。

<template>
  <div class="container">
    <div id="firebaseui-auth-container" />
  </div>
</template>

<script lang="ts">
import { Component, Vue } from "nuxt-property-decorator";

// 初期化済みのfirebaseインスタンス。詳細は略
import firebase from "~/plugins/firebase";

// Firebase UI
import * as firebaseui from "firebaseui-ja";

// CSSのインポート。これがないとレイアウトが崩れる
require("firebaseui-ja/dist/firebaseui.css");

@Component
export default class LoginPage extends Vue {
  private beforePath: string | null = null;
  
  asyncData({ store, redirect, query }) {
    let path = null;
    if (!!query.next) {
      // クエリパラメタが存在したら取得する
      path = query.next.replace(/%2F/g, "/");
    }
    
    return { beforePath: path }
  }

  beforeRouteEnter(to, from, next) {
    // Vueコンポーネントにアクセスする場合は、nextのコールバック内で行う。
    next(component => {
      if (!!from.name && from.name != "index") {
        // パスに'/'が入っているので置き換え(エンコード)
        const path = from.path.replace(/\//g, "%2F");
        // '/login?next=<前のパス>'になるように設定
        component.$router.push({ query: { next: path } });
      }
    });
  }

  mounted() {
    const auth = firebase.auth();
    const ui =
      firebaseui.auth.AuthUI.getInstance() || new firebaseui.auth.AuthUI(auth);

    const uiConfig: firebaseui.auth.Config = {
      callbacks: {
        // 認証成功時のコールバック
        signInSuccessWithAuthResult: (
          authResult: firebase.auth.UserCredential,
          redirectUrl: string
        ) => {
          // 戻り先が存在したら、遷移する
          if (!!this.beforePath) this.$router.push(this.beforePath)
          
          // firebase UI側でリダイレクトしないようにfalseを返す
          return false;
        },
        // 認証成功時のコールバック
        signInFailure: async (error: firebaseui.auth.AuthUIError) => {
          console.warn("signInFailure", error);
        },
        // UI表示時のコールバック
        uiShown: () => {
          console.info("uiShown");
        }
      },
      signInFlow: "redirect",
      signInOptions: [
        firebase.auth.TwitterAuthProvider.PROVIDER_ID,
        firebase.auth.GoogleAuthProvider.PROVIDER_ID
      ],
      tosUrl: "...",          // 利用規約のURL
      privacyPolicyUrl: "..." // プライバシーポリシーのURL
    };

    // Firebase UIを表示させたいIDを指定する
    ui.start("#firebaseui-auth-container", uiConfig);
  }
}
</script>

Firebase Authのリダイレクトだと、

  1. 各プロバイダの認証画面に遷移
  2. ログイン画面に再アクセス

となるので、Storeにもデータを残せない。。

ただ、クエリパラメタをつけておくと、
クエリパラメタつけたままログイン画面に戻ってくるので、それを利用。

また、signInSuccessUrlを設定しておけばいいかと思ったら、
signInSuccessWithAuthResultがPromiseを返せないのでこんな感じに。

認証成功時に、Firestoreにあるデータをロードしたかったので、ちょっと手間っぽい。

以上!!

参考にしたサイト様