jetpack composeで使うFirebase Authentication

# はじめに
最近jetpack composeでFirebaseを始めたんですが、なかなか日本語の記事がヒットしなかったり、構造複雑すぎじゃね！？とかが多かったので今回はFirebase初学者向けに簡単に実装できて色々と応用が効くように実装したいと思います。
# 参考資料

https://youtu.be/YcAUD1rLJPg?si=BCg_jY9RMLOC0uyN

英語の動画ですがコードだけ参考にする程度であれば字幕つけるだけで十分に理解出来ます。

https://www.anthropic.com/news/claude-3-5-sonnet

こちらはFirebaseAuthを使う際に構造を理解するために使いました。
丸々コピペより構造を理解しながら使うのがベストかも、、、

# 実装
***１***\.Android Studioを立ち上げたら、Macだったら上部の(Windowsであったら左上の三本線を押して上部の)toolsからFirebaseを選択します。

***2***\.したらAuthenticatonを選択して①の**ConneConnect your app to Firebase**を完了させて下さい。

:::note info
困ったらこちらを参考にしたら間違い無いです！
https://firebase.google.com/docs/android/setup?hl=ja
:::

***3***\.そしたらProjectレベルのbuild.gradle.ktxに下記が追加されているか確認して下さい
```kotlin:build gradle.ktx
plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.jetbrains.kotlin.android) apply false

    id("com.google.gms.google-services") version "4.4.2" apply false
}
```
***4***\.Moduleレベルのbuild.gradle.ktxに追加して下さい
```kotlin:build.gradle.ktx
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.jetbrains.kotlin.android)

    id("com.google.gms.google-services")
}
/*,,,,*/

dependencies {
    /*,,,,*/

    //navigation
    implementation("androidx.navigation:navigation-compose:2.7.7")

    //coil
    implementation("io.coil-kt:coil-compose:2.4.0")
    
    // Firebase
    implementation(platform("com.google.firebase:firebase-bom:33.1.1"))
    implementation("com.google.firebase:firebase-analytics")
    // toolsのFirebaseからAuthenticationの②から自動で追加されます。
    implementation(libs.firebase.auth)


    // Credential Manager
    implementation ("androidx.credentials:credentials:1.2.2")
    implementation ("androidx.credentials:credentials-play-services-auth:1.2.2")
    implementation ("com.google.android.libraries.identity.googleid:googleid:1.1.0")

}
```
***5***\.次にFirebaseの方でAuthenticationからログイン方法を選択し、新しいプロバイダを追加でgoogleを選択する。

そしたら、MainActivity.kt以外に**LoginScreen.kt**と**HomeScreen.kt**を追加して下さい。
追加したらまずHomeScreen.ktとLoginScreen.ktを下記のとおりにして下さい。
```kotlin:HomeScreen.kt
@Composable
fun HomeScreen(
    currentUser:FirebaseUser?,
    onSignOut:() -> Unit,
){
    val textStyle = TextStyle(
        fontWeight = FontWeight.Medium,
        fontFamily = FontFamily.Monospace,
        fontSize = 16.sp
    )

    Surface(
        modifier = Modifier
            .fillMaxSize(),
    ) {
        Column(
            modifier = Modifier
                .fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            currentUser?.let { user ->
                user.photoUrl?.let {
                    AsyncImage(
                        model = ImageRequest.Builder(LocalContext.current)
                            .data(it)
                            .crossfade(true)
                            .build(),
                        contentDescription = "profile photo",
                        contentScale = ContentScale.Crop,
                        modifier = Modifier
                            .size(160.dp)
                            .clip(RoundedCornerShape(4.dp))
                    )
                    Spacer(modifier = Modifier.size(16.dp))
                }
                user.displayName?.let{
                    Text(
                        text = it,
                        style = textStyle,
                        overflow = TextOverflow.Ellipsis,
                        maxLines = 1
                    )
                    Spacer(modifier = Modifier.size(16.dp))
                }

                user.email?.let{
                    Text(
                        text = "Mail ID'$it",
                        style = textStyle,
                        overflow = TextOverflow.Ellipsis,
                        maxLines = 1
                    )
                    Spacer(modifier = Modifier.size(16.dp))
                }

                Text(
                    text = "UID:${user.uid}",
                    style = textStyle,
                    overflow = TextOverflow.Ellipsis,
                    maxLines = 1,
                    )
                Spacer(modifier = Modifier.size(16.dp))

                Button(onClick = onSignOut) {
                    Text(
                        text = stringResource(id = R.string.sign_out),
                        style = textStyle.copy(
                            fontWeight = FontWeight.SemiBold
                        )
                    )
                }
            }
        }
    }
}
```
```kotlin:LoginScreen.kt

@Composable
fun LoginScreen(
    onSignInClick:() -> Unit,
){
    Surface(
        modifier = modifier
            .fillMaxSize()
    ) {
        Column(
            modifier = Modifier
                .fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Icon(
                imageVector = Icons.Default.Home,
                contentDescription = null,
                modifier = Modifier
                    .size(80.dp)
            )
            Spacer(modifier = Modifier.height(32.dp))
            
            Button(onClick = onSignInClick) {
                Text(text = stringResource(id = R.string.sign_in))
            }
        }
    }
}
```
***6***\.ここまでは理解出来たと思います。ここからがFirebaseで実装する大部分です。
Navigation.ktを作成して下さい。Navigation.ktにFirebaseAuthを実装していきたいと思います。
```kotlin:Navigation.kt
@Composable
fun Navigation(
    navHostController: NavHostController,
    FirebaseViewModel:FirebaseViewModel,
    scope:CoroutineScope,
){
    val startDestination = if(FirebaseViewModel.auth.currentUser == null) Screen.Login.name else
        Screen.Home.name
    val context = LocalContext.current
    val credentialManager = CredentialManager.create(context)


    NavHost(
    navController = navHostController,
    startDestination = startDestination
    ){
        composable(Screen.Login.name){
            LoginScreen(
                onSignInClick = {
                    val googleIdOption = GetGoogleIdOption.Builder()
                        .setFilterByAuthorizedAccounts(false)
                         //WEB_CLIENT_IDはあとでMainActivityに書きます
                        .setServerClientId(WEB_CLIENT_ID)
                        .build()

                    val request = GetCredentialRequest.Builder()
                        .addCredentialOption(googleIdOption)
                        .build()

                    scope.launch {
                        try {
                            val result = credentialManager.getCredential(
                                request = request,
                                context = context
                            )
                            val credential = result.credential
                            val googleIdTokenCredential = GoogleIdTokenCredential
                                .createFrom(credential.data)
                            val googleIdToken = googleIdTokenCredential.idToken
                            val firebaseCredential =
                                GoogleAuthProvider.getCredential(googleIdToken,null)
                            FirebaseViewModel.auth.signInWithCredential(firebaseCredential)
                                .addOnCompleteListener{ task ->
                                    if(task.isSuccessful){
                                        navHostController.popBackStack()
                                        navHostController.navigate(Screen.Home.name)
                                    }
                                }
                        }catch (e:Exception){
                            Toast.makeText(
                                context,
                                "Error:${e.message}",
                                Toast.LENGTH_LONG).show()
                            e.printStackTrace()
                        }
                    }
                }
            )
        }
        composable(Screen.Home.name){
            HomeScreen(
                currentUser = FirebaseViewModel.auth.currentUser,
                onSignOut = {
                    FirebaseViewModel.auth.signOut()
                    scope.launch {
                        credentialManager.clearCredentialState(
                            ClearCredentialStateRequest()
                        )
                        navHostController.popBackStack()
                        navHostController.navigate(Screen.Login.name)
                    }
                }
            )
        }
    }
}

enum class Screen{
    Login,
    Home
}
```
***6***\.最後にFirebaseViewModelを作成して、FirebaseViewModelとMainAcitivityに追記していきたいと思います。
```kotlin: FirebaseViewModel.kt
class FirebaseViewModel:ViewModel(){
    val auth: FirebaseAuth = FirebaseAuth.getInstance()
}
```
```kotlin: MainActivity.kt
// Firebaseを開いてAuthenticationのログイン方法の画面からGoogleを選んで
// その中からウェブSDK構成のウェブクライアントIDをコピーすれば良い
const val WEB_CLIENT_ID = "...."



class MainActivity : ComponentActivity() {


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            val navController = rememberNavController()
            val scope = rememberCoroutineScope()
            val FirebaseAuthViewModel = FirebaseViewModel()

            Navigation(
                navHostController = navController,
                FirebaseViewModel = FirebaseAuthViewModel,
                scope = scope,
            )

        }
    }
}
```

# 終わり
以上になります。
これでGoogleのアカウントでログインできるようになっているはずです。（多分...）

この記事は2024年6月28日に書いていますが、もしかしたら推奨されていない形になっているかもしれません。
そこのところは考慮して下さい。よろしくお願いします。
また、問題点などが発覚したら気兼ねなくお知らせください。