2
0

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.

JetpackComposeでFirebaseAuth(Google認証)

Last updated at Posted at 2023-07-28

Googleアカウント認証+FirebaseAuth with JetpackCompose

OneTapAuthじゃない方(今までのやり方)のGoogle認証+FirebaseAuth認証を実装していきます。
OneTap認証で実装したクラスを拡張していきます。

関連記事

エンドポイント

Googleアカウントのエンドポイント

エンドポイント
googleSignInClient.signInIntent Googleアカウント認証画面(アカウント一覧が出る画面)を表示します
GoogleSignIn.getSignedInAccountFromIntent 認証画面の戻り値をGoogleAccountに変換します

FirebaseAuthのエンドポイント

エンドポイント
GoogleAuthProvider.getCredential(idToken, accessToken) GoogleアカウントのidTokenからFirebaseAuthのCredentialに取得します
Firebase.auth.signInWithCredential(firebaseCredential) FirebaseAuthのCredentialでサインイン処理を行います

処理の流れ

流れはフローチャートにしました。OneTap認証とほぼ同じ流れになります。
GoogleAuthLegacy.png

build.gradle

app/build.gradle
android {
+    def properties = new Properties()
+    properties.load(project.rootProject.file('local.properties').newDataInputStream())
+    def googleOauthServerClientId = properties.getProperty('google_oauth_server_client_id')

+    defaultConfig {
+        buildConfigField("String", "GOOGLE_OAUTH_SERVER_CLIENT_ID", "${googleOauthServerClientId}")
+    }
}

+    // for GoogleSignIn
+    implementation 'com.google.android.gms:play-services-auth:20.6.0'

def googleOauthServerClientId = properties.getProperty('google_oauth_server_client_id')
buildConfigField("String", "GOOGLE_OAUTH_SERVER_CLIENT_ID", "${googleOauthServerClientId}")

GoogleアカウントとFirebaseAuthを紐付けるのに、GoogleアカウントのIdTokenが必要です。
このIdTokenを取得するには、GoogleCloudConsole>対象アプリ>認証情報>OAuth 2.0 クライアント IDのうち、Web clientのクライアントIDが必要になります。
秘密の情報にしておいた方が無難かと思いますので、クライアントIDは「local.properties」に保存しておいて、kotlinからはBuildConfig経由で取得できるようにするための設定になります。

implementation 'com.google.android.gms:play-services-auth:20.6.0'

Googleアカウントの認証には、play-services-authが必要です。

DIの実装

Googleアカウントの認証に使用するGoogleSignInなどのインスタンス生成を追記します。
GoogleSignInOptions.Builder().requestIdToken(oauth_web_client_id)を呼び出すと、GoogleアカウントのIdTokenを取得できるようになります。

FirebaseModule.kt
@Module
@InstallIn(SingletonComponent::class)
object FirebaseModule {
    @Provides
    fun auth(): FirebaseAuth = Firebase.auth

    /** Google認証(OneTap)用の認証画面構築クラス */
    @Provides
    fun provideBeginSignInRequest(): BeginSignInRequest =
        BeginSignInRequest.builder()
            .setGoogleIdTokenRequestOptions(
                BeginSignInRequest.GoogleIdTokenRequestOptions.builder()
                    .setSupported(true)
                    .setServerClientId(BuildConfig.GOOGLE_OAUTH_SERVER_CLIENT_ID)
                    .setFilterByAuthorizedAccounts(true)
                    .build()
            )
            .build()

    /** Google認証(OneTap)用の認証クライアント */
    @Provides
    fun provideSignInClient(
        @ApplicationContext context: Context,
    ): SignInClient = Identity.getSignInClient(context)

+    /** Google認証(Legacy)用のオプション定義 */
+    @Provides
+    fun provideGoogleSignInOptions(): GoogleSignInOptions =
+        GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
+            .requestIdToken(BuildConfig.GOOGLE_OAUTH_SERVER_CLIENT_ID)
+            .build()

+    /** Google認証(Legacy)用の認証クライアント */
+    @Provides
+    fun provideGoogleSignInClient(
+        @ApplicationContext context: Context,
+        googleSignInOptions: GoogleSignInOptions,
+    ): GoogleSignInClient = GoogleSignIn.getClient(context, googleSignInOptions)
}

インフラ層の実装

Googleアカウント認証画面を起動するためのIntentを生成する関数と、
認証画面からの戻り値を使用してFirebaseAuthでsigninする関数の2つを追加します。
処理の流れは、前述のフローチャートの通りです。

AccountRepository.kt
+    fun requestGoogleLegacyAuth(): Intent
+    suspend fun signinGoogleLegacy(resultData: Intent)
AccountRepositoryImpl.kt
class AccountRepositoryImpl @Inject constructor(
    private val auth: FirebaseAuth,
    private val signInRequest: BeginSignInRequest,
    private val signInClient: SignInClient,
+    private val googleSignInClient: GoogleSignInClient,
) : AccountRepository {
+    override fun requestGoogleLegacyAuth(): Intent {
+        return googleSignInClient.signInIntent
+    }

+    override suspend fun signinGoogleLegacy(resultData: Intent) {
+        val googleSignInAccount: GoogleSignInAccount = GoogleSignIn.getSignedInAccountFromIntent(resultData).await()
+        val firebaseCredential = GoogleAuthProvider.getCredential(googleSignInAccount.idToken, null)
+        auth.signInWithCredential(firebaseCredential).await()
+    }
}

UseCaseの実装

GoogleLegacySigninCase.kt
class GoogleLegacySigninCase @Inject constructor(
    private val accountRepository: AccountRepository,
) {
    // ①signin関数
    fun signin(launcher: ActivityResultLauncher<Intent>) {
        val intent = accountRepository.requestGoogleLegacyAuth()
        launcher.launch(intent)
    }

    // ②onResultSignin関数
    suspend fun onResultSignin(result: ActivityResult) {
        if (result.resultCode != Activity.RESULT_OK) {
            Log.d("GoogleLegacySigninCase", "onResultSignin ${SigninGoogleLegacyError(result.data).resultMessage}")
            return
        }

        val resultData = result.data ?: let {
            Log.d("GoogleLegacySigninCase", "onResultSignin result.data is null")
            return
        }

        accountRepository.signinGoogleLegacy(resultData)
    }
}

①signin関数
Google認証用のアカウント一覧画面を起動します。

②onResultSignin関数
Google認証用のアカウント一覧画面の戻り値を処理します。
result.resultCodeがActivity.RESULT_OKの場合は、Signin処理を行っています

ViewModelの実装

これはUseCaseを呼び出すだけなので説明は割愛です。

SigninViewModel.kt
@HiltViewModel
class SigninViewModel @Inject constructor(
    private val signinUsecase: SigninUsecase,
    private val signupUsecase: SignupUsecase,
    private val signOutUsecase: SignOutUsecase,
    private val googleOneTapSigninCase: GoogleOneTapSigninCase,
+    private val googleLegacySigninCase: GoogleLegacySigninCase,
    accountRepository: AccountRepository,
) : ViewModel() {
    /* ...いろいろ実装... */

+    fun onClickSignInWithGoogleLegacy(launcher: ActivityResultLauncher<Intent>) {
+        try {
+            googleLegacySigninCase.signin(launcher)
+        } catch (e: Exception) {
+            val message = e.toSnackbarMessage()
+            SnackbarManager.showMessage(e.toSnackbarMessage())
+        }
+    }

+    fun onResultSignInWithGoogleLegacy(result: ActivityResult) {
+        viewModelScope.launch(
+            context = CoroutineExceptionHandler { _, throwable ->
+                val message = throwable.toSnackbarMessage()
+                SnackbarManager.showMessage(throwable.toSnackbarMessage())
+            },
+            block = {
+                googleLegacySigninCase.onResultSignin(result)
+            },
+        )
+    }

Screenの実装

SigninScreen.kt
@Composable
+ private fun GoogleLegacySigninButton(viewModel: SigninViewModel) {
+    // ① Google認証画面ランチャの生成
+    val startForResult = rememberLauncherForActivityResult(
+        contract = ActivityResultContracts.StartActivityForResult(),
+        onResult = viewModel::onResultSignInWithGoogleLegacy,
+    )
+
+    BasicButton(
+        text = R.string.sign_in_with_google_legacy,
+        modifier = Modifier.basicButton(),
+    ) {
+        // ② ボタンクリック
+        viewModel.onClickSignInWithGoogleLegacy(startForResult)
+    }
+ }

① Google認証画面ランチャの生成
rememberLauncherForActivityResultを使って、ActivityResultLauncherを作成します。
ActivityResultLauncherのonResultに、「viewModel.onResultSignInWithGoogleLegacy」を指定して、結果処理を行います。

② ボタンクリック
Googleアカウント認証画面を起動するトリガーです

いざ実行

コードはこちらです。
google_1.pnggoogle_2.pnggoogle_3.png

2
0
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
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?