概要(日記)
の続き。ユーザ認証など
前提
- 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() }
}
}
}
}
}
}
資料