メモ的な感じでGoogleログイン認証をTypeScriptで実装した内容をまとめます。認証はFirebase Authenticationを使用しています。
UseCase層でGoogle認証のロジックを実装する
流れとしてはFirebase AuthenticationでGoogle認証した後、DBにユーザー情報を問い合わせます。
ユーザー情報がなかったら新規登録としてユーザ登録します。ユーザーログイン認証完了後はmypageに遷移し、認証情報をstateに登録します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | async signInWithGoogle(auth: Auth) { const userWithGoogle: CredentialUser = await this.authGateway.signInWithGoogle(auth) const userAccount: UserAccount | null = await this.userGateway.getUserByUid(userWithGoogle.uid) if (userAccount) { this.toastPresenter.successToast(MESSAGES.AUTH.LOGIN_SUCCESS) } else { await this.userGateway.createUser(userWithGoogle) this.toastPresenter.successToast(MESSAGES.AUTH.REGISTER_SUCCESS) } await this.storeAuthState(userWithGoogle.uid) this.navigationPresenter.navigateTo('/mypage') } catch(error: Error) { console.error('signInWithGoogle error: ', error); this.toastPresenter.errorToast(`${MESSAGES.UNEXPECTED_ERROR}: ${error.message}`) } private async storeAuthState(uid: string) { const authAccount = await this.userGateway.getUserByUid(uid) this.authPresenter.storeAuth(authAccount!) }ち |
ちなみにUseCaseのUTはこんな感じになりました。あと登録パターンなどのシナリオがいくつかあるといった感じです。
Clean Architecutureをベースにレイヤリングしているので、テストは書きやすかったです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | it('Google認証でログインすることができる', async () => { const auth = mock<Auth>(); const signInWithGoogleMock = jest.fn() when(signInWithGoogleMock).calledWith(auth).mockReturnValueOnce(credentialUser) authGateway.signInWithGoogle = signInWithGoogleMock const getUserByUidMock = jest.fn() when(getUserByUidMock).calledWith(credentialUser.uid).mockReturnValueOnce(userAccount) userGateway.getUserByUid = getUserByUidMock const successToastMock = jest.fn() when(successToastMock).calledWith(MESSAGES.AUTH.LOGIN_SUCCESS) toastPresenter.successToast = successToastMock const navigateToMock = jest.fn() when(navigateToMock).calledWith('/mypage') navigationPresenter.navigateTo = navigateToMock await authUsecase.signInWithGoogle(auth); expect(authGateway.signInWithGoogle).nthCalledWith(1, auth); expect(userGateway.getUserByUid).nthCalledWith(1, credentialUser.uid); expect(toastPresenter.successToast).nthCalledWith(1, MESSAGES.AUTH.LOGIN_SUCCESS); }); |
GatewayでDomainに変換する
UseCaseのthis.authGateway.signInWithGoogle(auth)でFirebase AuthenticationにGoogle認証しているところをピックアップして、Gatewayの実装を取り上げます。
Gatewayとしては、DriverでサードパーティであるFirebase Authenticationを呼び出した返り値を、このアプリケーション内のコアとなるDomainに変換してUseCaseに返します。
つまり、Gatewayが外部とアプリケーションの境界部分を担っている感じです。今回は、CredentialUserというDomainに変換して、UseCaseに返しています。
1 2 3 4 5 6 7 8 9 | async signInWithGoogle(auth: Auth): Promise<CredentialUser> { const userCredentialWithGoogle = await this.authDriver.signInWithGoogle(auth) return { uid: userCredentialWithGoogle.user.uid, displayName: userCredentialWithGoogle.user.displayName, email: userCredentialWithGoogle.user.email, photoURL: userCredentialWithGoogle.user.photoURL, } as CredentialUser } |
Driverで実際にGoogle認証する
実際にGoogle認証するのはこのDriverになります。DriverではGoogleAuthProvider()からsignInWithPopup()を呼び出し、認証した結果を返します。
本来はDriverで使用するエンティティを用意して、その型に詰めてからGatewayに返してあげる方が、アプリケーションとサードパーティとの切り分けができてより良い気もしますが、一旦ここではそのままGatewayに返しています。
1 2 3 4 5 | async signInWithGoogle(auth: Auth): Promise<UserCredential> { const provider = new GoogleAuthProvider() const userCredentialWithGoogle = await signInWithPopup(auth, provider) return userCredentialWithGoogle } |
すんごいざっくりですが、メモ的な感じでまとめました。