120% Firebase Auth의 힘을 끌어내기 위한 해커집

Ubie Discovery의.
증상 검색 엔진\"\"은Firebase Auth(GCP Identity Plaatform)를 사용합니다.이 가운데 Firebase Auth의 비전을 뛰어넘는 사용법을 소개하고, 이를 실현하기 위한 무리한 해커들을 소개했다.

Capacitor에서 OAuth 이동


Capacitor(=WebView)에서 웹 브라우저와 같이 OAuth를 진행하려면 다음과 같은 문제에 직면하게 된다.
  • Google과 같은 인증 제공자는 WebView에서 액세스할 수 있음(참조)
  • 인증 공급자로부터 호출이 터미널의 기본 브라우저에서 열려 로컬 응용 프로그램으로 돌아갈 수 없음
  • 캐피톨의 유사기술Cordova에서도 같은 문제가 있었고, 파이어베이스 JS SDK는 코도바뒷받침를 적용했다.이를 어떻게 구현했느냐면 코도바가 글로벌window에서 로컬 기능을 조작하는 함수를 생성했고, 파이어베이스 JS SDK가 이를 두드려 다음과 같은 솔루션을 구현했다.
  • WebView가 아닌 In-App 브라우저에서 인증 공급자 열기
  • 호출Dynamic Link을 통해 로컬 애플리케이션
  • 으로 반환
    Firebase JS SDK에서 기대하는window 인터페이스가 여기에 정의되어 있습니다.
    https://github.com/firebase/firebase-js-sdk/blob/2820674b848e918ab164e7d0ec9d5b838bbfa6e0/packages/auth/src/platform_cordova/plugins.ts#L18-L46
    이를 참고로 같은 인터페이스에서 캡쳐를 호출하는 함수 그룹 주입window을 통해 코도바 지원을 캡쳐에 강제로 적용할 수 있다.
    import { App } from "@capacitor/app";
    import { Browser } from "@capacitor/browser";
    
    export async function initCapacitorWindow(): Promise<void> {
      const appInfo = await App.getInfo();
    
      window.BuildInfo = {
        packageName: appInfo.id,
        displayName: appInfo.name,
      };
    
      window.universalLinks = {
        subscribe: (
          _: null,
          cb: (event: Record<string, string> | null) => void
        ) => {
          App.addListener("appUrlOpen", (data) => {
            cb({ url: data.url });
          });
        },
      };
    
      window.cordova.plugins = window.cordova.plugins || {};
      window.cordova.plugins.browsertab = {
        openUrl: (url: string) => {
          Browser.open({ url });
        },
        close: () => {
          Browser.close();
        },
        isAvailable(cb: (available: boolean) => void) {
          cb(true);
        },
      };
    
      window.cordova.InAppBrowser = {
        open() {
          // `window.cordova.plugins.browsertab` が優先されるため、これは呼ばれない
          throw new Error("Unexpected window.cordova.InAppBrowser call");
        },
      };
    }
    
    유지 보수가 힘들 것 같아요.

    OAuth의 콜백 대상을 동적으로 설정합니다.

    signInWithRedirect 또는 linkWithRedirect에서 리디렉션 인증을 할 때 리콜을 명확하게 지정할 수 없고 signInWithRedirect 또는 linkWithRedirect를 호출할 때 페이지의 리콜을 지정할 수 없습니다.각 인증 공급자redirect_uri에게Firebase가 제공하는 호출 페이지가 되어 그 페이지를 끼워서 되돌려줍니다.
    호출 대상을 동적으로 설정하기 위해서는 Firebase 호출 전용 프록시 페이지를 만드는 것이 좋습니다.다음은 Next입니다.js의 예.
    auth-redirect.tsx
    import { NextPage } from 'next';
    import { useEffect } from 'react';
    import { useRouter } from 'next/router';
    import * as firebaseAuth from 'firebase/auth';
    
    const AuthRedirectPage: NextPage = () => {
      const router = useRouter();
    
      useEffect(() => {
        if (!router.isReady) {
          return;
        }
    
        (async () => {
          const result = await firebaseAuth.getRedirectResult(firebaseAuth.getAuth());
          if (result == null) {
            // result がない時は認証前
    	// `auth/redirect-cancelled-by-user` 等のエラー検証が必要だが、ここでは省略
            await firebaseAuth.signInWithRedirect(firebaseAuth.getAuth(), new firebaseAuth.GoogleAuthProvider());
          } else {
            // result がある時は認証済み
            // オープンリダイレクタ等を回避するために検証が必要だが、ここでは省略
            const redirectUri = router.query['redirect_uri'] as string | undefined;
            router.push(redirectUri || '/');
          }
        })();
      }, [router.isReady]);
    
      return <Loading />;
    };
    
    export default AuthRedirectPage;
    
    다음 절차에 따라 사용하세요.
  • 소개 페이지에서 /auth-redirect?redirect_uri=<ログイン後に遷移するURL>
  • 로 마이그레이션
  • getRedirectResult가 텅 비고signInWithRedirect 인증 제공자
  • 로 날아간다
  • 인증 제공자에서Firebase/auth-redirect?redirect_uri=<ログイン後に遷移するURL>로 호출
  • getRedirectResultnon-null로 변경redirect_uri
  • OAuth에서 처음 로그인한 경우에만 링크


    Firebase Auth에서는 현재 로그인한 계정에 다른 인증 공급자링크를 설정할 수 있습니다.
    Ubi에 가입자가 없는 사용자도 Firebase에서익명 사용자로 처리되며, 가입자를 등록할 때 링크를 통해 등록 전의 데이터를 유지한다.
    이때 Email/Password 인증에서 createUserWithEmailAndPassword 대신 linkWithCredential를 사용하여 기존의 익명 사용자를 연결할 수 있다.그러나 OAuth에서 신규 로그인과 로그인은 차이가 없어 진행linkWithRedirect할지 진행signInWithRedirect할지 결정할 수 없다.
    따라서 먼저 시도linkWithRedirect하고 실패할 때signInWithCredential후진한다.이렇게 하면'처음 로그인할 때 링크하고 나중에 로그인'하는 행위를 실현할 수 있다.
    import { FirebaseError } from "firebase/app";
    import * as firebaseAuth from "firebase/auth";
    
    try {
      const currentUser = firebaseAuth.getAuth().currentUser;
      const provider = new firebaseAuth.GoogleAuthProvider();
      await firebaseAuth.linkWithRedirect(currentUser, provider);
    } catch (e: unknown) {
      if (
        e instanceof FirebaseError &&
        e.code === "auth/credential-already-in-use"
      ) {
        // 既に使われている認証情報(=リンク済み)の時はそれを使ってログイン
        const credential = firebaseAuth.OAuthProvider.credentialFromError(e);
        if (!credential) {
          throw new Error("no credential");
        }
        await firebaseAuth.signInWithCredential(firebaseAuth.getAuth(), credential);
      } else {
        throw e;
      }
    }
    

    결론


    120%를 끌어내면 힘들고 언제 망가질지 모르니 끌어내지 않는 게 좋다.Ubie는 인증 기반 내부 생산을 추진 중이다.

    좋은 웹페이지 즐겨찾기