0
0

Kotlin Multipatform とFirebaseでユーザ認証

Last updated at Posted at 2024-07-31

概要(日記)

の続き。ユーザ認証など

前提

  • Ubuntu 22.04 on Windwos 11 wsl2
  • Firebaseプロジェクト・AuthenticationとFirestoreが有効であること
  • Kotlin/js、UIはkotlinx-html

Authenticationのユーザ登録

Authentication → ユーザー → ユーザーを追加
→ メールアドレスとログインパスワードを入力

ログインページ

Firebase認証済みかをチェックし、未認証ならログインページ、認証済みならアプリページを表示する。

build.gradle.kts
//...
kotlin {
    js {
        browser {}
        binaries.executable()
    }
    sourceSets {
        jsMain.dependencies {
            implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") // https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core
            implementation("dev.gitlive:firebase-firestore:1.13.0") // https://mvnrepository.com/artifact/dev.gitlive/firebase-firestore
            implementation("dev.gitlive:firebase-auth:1.13.0") // https://mvnrepository.com/artifact/dev.gitlive/firebase-auth
            implementation("org.jetbrains.kotlinx:kotlinx-html:0.11.0") // https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-html
        }
    }
}
src/jsMain/resources/index.html
<script src="Login.js"></script>

src="Login.js"はProject名(デフォルトでプロジェクトフォルダ名)に置き換え

src/jsMain/kotlin/Main.kt
val options = FirebaseOptions(
    apiKey = "AIzaSyBg5ssUSPQlEKxZ6zoBrg-hwhoMzwWLQPQ",
    projectId = "riot-7a79a",
    applicationId = "1:749774078339:web:9d60dff9671ab8e9ad76b6",
)
val app = Firebase.initialize(Unit, options)
val auth = Firebase.auth(app)

val db = Firebase.firestore(app).apply {
    settings = firestoreSettings(settings) { cacheSettings = persistentCacheSettings { } }
}

suspend fun main() = Firebase.auth(app).authStateChanged.collect { user ->
    when (user) {
        null -> loginPage()
        else -> appPage(user)
    }
}

fun <T> TagConsumer<T>.inputx(opt: INPUT.() -> Unit = {}, chg: suspend (v: String) -> Unit = {}) = input {
    opt()
    onChangeFunction = { MainScope().launch { chg((it.target as HTMLInputElement).value) } }
}

suspend fun loginPage() = document.body!!.apply { clear() }.append {
    var userId = ""
    var password = ""
    fun login() = MainScope().launch {
        runCatching { auth.signInWithEmailAndPassword(userId, password) }.onFailure { window.alert("Failed.") }
    }
    p { +"USER ID:"; inputx { userId = it } }
    p { +"PASSWORD:"; inputx({ type = InputType.password }) { password = it } }
    p { button { +"LOGIN"; onClickFunction = { login() } } }
}

※AppKeyなどの接続情報は Firebaseポータル→プロジェクトの設定 を参照。

アプリページ

自分のコメント(ステータス)をメンバーで共有するアプリ。

ユーザー認証に関するシナリオ例:

  • 自分のドキュメントは修正できる
  • (ログインすれば)他人のドキュメントは参照できる

自分のコメントのみ編集できることを確認

Firestore Rule
rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read: if request.auth.uid!=null;
      allow create: if request.auth.uid == request.resource.data.uid;
      allow update, delete: if request.auth.uid == resource.data.uid;
    }
  }
}
src/jsMain/kotlin/Main.kt
//...
suspend fun appPage(user: FirebaseUser) = document.body!!.apply { clear() }.append {
    @Serializable
    data class Status(val uid: String, val email: String, val status: String, val time: Instant = now())

    fun initStatus() = Status(user.uid, user.email ?: "", "")
    suspend fun errCk(op: suspend () -> Unit) = runCatching { op() }.onFailure { window.alert("${it.message}") }
    val refAppRoot = db.collection("fireshell")

    val table = document.create.table()
    p { button { +"LOGOUT"; onClickFunction = { MainScope().launch { auth.signOut() } } }; +"${user.email}" }
    document.body!!.append(table)
    MainScope().launch {
        refAppRoot.document(user.uid).apply { if (!get().exists) set(initStatus()) }
        refAppRoot.snapshots.collect { qs ->
            table.clear()
            table.className = "table"
            table.append {
                qs.documents.filter { it.exists }.forEach { ds ->
                    val st = ds.data<Status>()
                    tr {
                        td { +st.email }
                        td { inputx({ value = st.status }) { errCk { ds.reference.set(st.copy(status = it)) } } }
                        td { +st.time.toLocalDateTime(TimeZone.currentSystemDefault()).toString() }
                    }
                }
            }
        }
    }
}

コード

資料

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