如何使用Firebase-操作Authentication-管理User(kotlin)

如何使用Firebase-操作Authentication-管理User(kotlin)

情境

如何使用 Firebase - 操作 Authentication - 註冊與登入帳戶,我們透過 Firebase 成功建立一個簡易版的 email 註冊系統,在這個系統我們其實可以增加更多管理的功能,例如: 修改 Email、修改密碼、Email 驗證以及刪除使用者 … 等等。

完整程式碼

你可以直接到 GitHub 上面觀看或者下載完整的程式碼。

程式碼說明

這邊拿如何使用 Firebase - 操作 Authentication - 註冊與登入帳戶 的範例來改寫,我們知道透過 FirebaseAuth 的物件就可以拿到已經登入後的 User 資訊。

val user = FirebaseAuth.getInstance().getCurrentUser()
if (user != null) {
 // User is signed in
} else {
 // No user is signed in
}

FirebaseUser 物件為 null 那就代表沒登入。

取得 User 資訊

你想要取得 User 的 profile 可以根據以下的方法來取得。

if (user != null) {
 //取得使用者名稱
    val name = user.getDisplayName()
    //取得使用者 email
    val email = user.getEmail()
    //取得使用者頭像位址
    val photoUrl = user.getPhotoUrl()
    //檢查是否有經過 email 驗證
    val emailVerified = user.isEmailVerified()
    //取得使用者 uid
    val uid = user.getUid()
}

所以我們可以將這些資訊顯示出來。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <TextView
        android:text="@string/login_success"
        android:id="@+id/main_info"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <Button
        android:layout_below="@id/main_info"
        android:text="@string/logout"
        android:id="@+id/logout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <Button
        android:layout_below="@id/logout"
        android:id="@+id/get_profile"
        android:text="取得 profile"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView
        android:layout_below="@id/get_profile"
        android:id="@+id/profile_info"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>

首先 layout 先排好,新增一個取得 profile 的資訊的按鈕,下方是顯示 profile 資訊的 TextView。

程式碼如下。

get_profile.setOnClickListener {  
 if (user != null) {  
  val sb = StringBuilder()  
  sb.append("display name:${user?.displayName}\n")  
  sb.append("email:${user?.email}\n")  
  sb.append("photo url:${user?.photoUrl}\n")  
  sb.append("is email verified:${user?.isEmailVerified}\n")  
  sb.append("uid:${user?.uid}")  
  profile_info.text = sb.toString()  
 }  
}

最後結果如下圖

你也可以透過 getProviderData() 來取得 User 資訊。

val user = FirebaseAuth.getInstance().currentUser  
if (user != null) {  
 for (profile in user.providerData) {  
  // Id of the provider (ex: google.com)  
  val providerId = profile.providerId  

  // UID specific to the provider  
  val uid = profile.uid  

  // Name, email address, and profile photo Url  
  val name = profile.displayName  
  val email = profile.email  
  val photoUrl = profile.photoUrl  
 }  
}

根據官方文件的說法,這個方法會取得不同 authentication 認證的 User 帳號資訊。

更新 User 資訊

在前面取得 profile 的資訊中,會發現我們的 photo url 以及 Display name 是空的,如果想要更新 profile 資訊,可以透過以下程式碼改變依些資訊,我們增加一個 Button 來操作更新使用者資訊。

在這邊我們要做一個 Dialog 讓使用者輸入他的名字,因此,客製化一個對話框,根據 如何使用 AlertDialog 改寫。

部分程式碼如下。

val item = LayoutInflater.from(this@MainActivity).inflate(R.layout.update_data_layout, null)  
AlertDialog.Builder(this@MainActivity)  
 .setView(item)  
 .setPositiveButton(R.string.ok) { _, _ ->  
  val displayName = item.findViewById<View>(R.id.display_name_edit) as EditText  
  val profileUpdates = UserProfileChangeRequest.Builder()  
 .setDisplayName(displayName.text.toString())  
 .setPhotoUri(Uri.parse("https://github.com/givemepassxd999/free_img/blob/master/01.jpg?raw=true"))  
 .build()  

 user?.updateProfile(profileUpdates)?.addOnCompleteListener { task ->  
  if (task.isSuccessful) {  
   Toast.makeText(this@MainActivity, "Profile 修改成功", Toast.LENGTH_SHORT).show()  
  } else {  
   Toast.makeText(this@MainActivity, "Profile 修改:" + task.exception?.toString(), Toast.LENGTH_SHORT).show()  
  }  
 }  
}.show()

至於更新 Firebase 可以參考下方程式碼。

val profileUpdates = UserProfileChangeRequest.Builder()  
 .setDisplayName(display_name_edit.text.toString())  
 .setPhotoUri(Uri.parse("https://github.com/givemepassxd999/free_img/blob/master/01.jpg?raw=true"))  
 .build()  

user?.updateProfile(profileUpdates)?.addOnCompleteListener { task ->  
 if (task.isSuccessful) {  
  Toast.makeText(this@MainActivity, "Profile 修改成功", Toast.LENGTH_SHORT).show()  
 } else {  
  Toast.makeText(this@MainActivity, "Profile 修改:" + task.exception?.toString(), Toast.LENGTH_SHORT).show()  
 }  
}

從上面程式碼得知,先從對話框取得使用者資訊,另外,photoUrl 因為路徑太長了就先寫死這個路徑(你可以自行修改成動態存取),宣告 UserProfileChangeRequest 物件,並且填入欲修改的資訊再傳入 FirebaseUser 物件 updateProfile 的方法。

透過 updateProfile.addOnCompleteListener 可以拿到是否成功的 callback,當修改成功後就會跳出顯示成功的視窗。

在按一次取得使用者資訊,你會發現無法更新,所以必須登入在登出才能出現新的 profile。

修改 Email 資訊

如果你想改變 email,可以透過以下程式碼。

val credential = EmailAuthProvider.getCredential("abc@gamil.com", "qazxsw")  
user?.reauthenticate(credential)?.addOnCompleteListener {  
 user?.updateEmail("def@gmail.com")?.addOnCompleteListener { task ->  
  if (task.isSuccessful) {  
   Toast.makeText(this@MainActivity, "Email 修改成功", Toast.LENGTH_SHORT).show()  
  } else {  
   Toast.makeText(this@MainActivity, "修改email:" + task.exception.toString(), Toast.LENGTH_SHORT).show()  
  }  
 }  
}
  • 官網強調當你刪除帳號、設定主要 email 以及改變密碼的時候,都必須做 Re-authenticate 否則有機會會丟出 FirebaseAuthRecentLoginRequiredException,因此,我們在呼叫 FirebaseUser 物件的 updateEmail,是寫在 FirebaseUser 物件的 addOnCompleteListener callback 當完成以後才可以改變 Email。

一開始我們在登入狀態之下先看一下 profile,可以看到我們原本的 Email 是 abc@gmail.com

接著按下調整 Email 的按鈕。

接著按下登出。

回到首頁我們再輸入剛剛調整的 Email : def@gmail.com

登入成功又回到剛剛登入的頁面。

修改完成。

  • 如果你要從 A Email 修改成 B Email 則會在 A Email 收到一封確認信。

驗證 Email

如果你需要驗證 Email 的功能,則可以透過以下的程式碼。

user?.sendEmailVerification()?.addOnCompleteListener { task ->  
 if (task.isSuccessful) {  
  Toast.makeText(this@MainActivity, "Email 驗證成功", Toast.LENGTH_SHORT).show()  
 } else {  
  Toast.makeText(this@MainActivity, "Email 驗證:" + task.exception.toString(), Toast.LENGTH_SHORT).show()  
 }  
}

先看一下 profile,是否驗證 Email 的欄位是 false。

接著按下驗證 Email按鈕。

到你的信箱去看,會收到一封認證信,按下連結。

Hello ,

Follow this link to verify your email address.

https://fir-authmanageruser.firebaseapp.com/__/auth/action?mode=verifyEmail&xxxxx

If you didn’t ask to verify this address, you can ignore this email.

Thanks,

Your FirebaseAuthManagerUser team

重新登出再登入,就會看到是否驗證 Email 的欄位是 true。

代表我們驗證成功。

修改密碼

如果想讓使用者修改密碼,則可以透過 FirebaseUser 物件的 updatePassword 方法,由於會讓使用者自行輸入要修改的密碼
新增一個修改密碼的按鈕。

因此,會先宣告一個 AlertDialog 來讓使用者輸入。

val item = LayoutInflater.from(this@MainActivity).inflate(R.layout.change_pw_layout, null)  
AlertDialog.Builder(this@MainActivity)  
 .setView(item)  
 .setPositiveButton(R.string.ok) {}
 .show()

當按下修改密碼就會跳出對話視窗。

在這邊我們將使用者輸入的新密碼傳入 FirebaseUser 物件的 updatePassword 方法。

val newPassword = new_pw.text.toString()  
user?.updatePassword(newPassword)?.addOnCompleteListener { task ->  
 if (task.isSuccessful) {  
  Toast.makeText(this@MainActivity, "Password 修改成功", Toast.LENGTH_SHORT).show()  
 } else {  
  Toast.makeText(this@MainActivity, "password 修改:" + task.exception.toString(), Toast.LENGTH_SHORT).show()  
 }  
}

輸入新密碼。

修改成功。

之後登出再登入時就可以使用新密碼登入了。

Re-authenticate

當你操作到刪除帳號、重新設定主要 Email 或者改變密碼的時候,就必須重新認證你的帳號,首先,必須透過 AuthCredential 拿到 EmailAuthProvider 的憑證,接著透過 FirebaseUser 物件的 reauthenticate 方法將憑證傳入,只要 callback 回來就代表我們 Re-authenticate 成功,先建立一個重新認證的按鈕。

val credential = EmailAuthProvider.getCredential("def@gamil.com", "wsxzaq")  
user?.reauthenticate(credential)?.addOnCompleteListener {  
 Toast.makeText(this@MainActivity, "重新認證成功", Toast.LENGTH_SHORT).show()  
}

當按下重新認證按鈕,會透過 reauthenticate 的 addOnCompleteListener 回傳結果,顯示重新認證成功的訊息。

刪除使用者

使用者想要刪掉自己的帳號,其實 Firebase 有提供這個服務,首先,先建立一個刪除的按鈕。

當按下按鈕則執行以下程式碼。

val credential = EmailAuthProvider.getCredential("def@gamil.com", "wsxzaq")  
user?.reauthenticate(credential)?.addOnCompleteListener {  
 user?.delete()?.addOnCompleteListener { task ->  
  if (task.isSuccessful) {  
   Toast.makeText(this@MainActivity, "User刪除成功", Toast.LENGTH_SHORT).show()  
  } else {  
   Toast.makeText(this@MainActivity, "User刪除:" + task.exception.toString(), Toast.LENGTH_SHORT).show()  
  }  
 }  
}

這樣就完成刪除帳號了。

到後台看到帳號的確被刪除了。

  • 如果無法刪除且出現訊息 credential TOO OLD 可能是憑證太舊 必須重新登入再進行刪除即可。

除了這些還有更多的管理上的功能,可以參考 FirebaseUser,這樣就是一個簡單的管理 User 的範例了。