NOTE: I was able to figure this out. There is no need to change the rules in Firebase. See code below.
ORIGINAL POST
I have an IOS app and I decided to build the Android/Kotlin version and I'm having a hard time with Firebase/isEmailVerify. I'm able to register a new user and send the email for verification, but, if I don't verify, I'm still able to login. I'm new at Kotlin. Any help is greatly appreciated.
UPDATED CODE
class LoginActivity : AppCompatActivity() {
lateinit var auth: FirebaseAuth
private var emailVerifier: Boolean = true
private val emailVerificationAlert = { _: DialogInterface, _: Int ->
Toast.makeText(this.applicationContext, android.R.string.yes, Toast.LENGTH_SHORT).show()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
auth = FirebaseAuth.getInstance()
}
private fun verifyEmail() {
val user = FirebaseAuth.getInstance().currentUser
if (user != null) {
emailVerifier = user.isEmailVerified()
}
if (emailVerifier) {
finish()
} else {
userDidNotVerify()
auth.signOut()
}
}
fun loginBtnClicked(view: View) {
val email = loginEmailTxt.text.toString()
val password = loginPasswordTxt.text.toString()
auth.signInWithEmailAndPassword(email, password)
.addOnSuccessListener { exception ->
println("USER LOGGED IN")
verifyEmail()
}
.addOnFailureListener { exception ->
Log.e("Exception", "Could not sign in user - ${exception.localizedMessage}")
}
}
private fun userDidNotVerify() {
val builder = android.app.AlertDialog.Builder(this)
with(builder) {
this.setTitle("Confirm your email address.")
this.setMessage("A confirmation email has been sent to" + " " + (loginEmailTxt.text) + " " +
"." + " " + "Click on the confirmation link to activate your account")
this.setPositiveButton("OK", DialogInterface.OnClickListener(function = emailVerificationAlert))
this.show()
}
}
fun loginCreateClicked(view: View) {
val createIntent = Intent(this, CreateUserActivity::class.java)
startActivity(createIntent)
}
}
It's expected that the user can still sign in before the email is verified. This provides a way for your app to allow the user to request another verification email to be sent, in case something happened to the first one.
If you want to restrict what the user can do before the email is verified, you can check isEmailVerfied() on the UserInfo object, and you can use the auth.token.email_verified in security rules to limit their access to databases and storage also provided by Firebase.
Related
How do I retrieve all the fields of the current user logged?
I've watched many tutorials and questions, and some of them talk about the whole collection, others about similar topics, but found no info about this.
Thank you
UPDATE
Current Code:
fun getUserName_FireBase(){
if(userID==null){
println("The userID is null")
userID= getUserID()
println("The userId has been assigned and now is: " + userID.toString())
}
println("1")
val db = FirebaseFirestore.getInstance()
println("1a")
val usersRef = db.collection("users")
println("1b")
usersRef.document(userID.toString()).get().addOnCompleteListener { task ->
println("2")
if (task.isSuccessful) {
println("3")
val document = task.result
if(document!!.exists()){
println("4")
userName = document!!.getString("user").toString()
println("user is " + userName.toString())
}else {
println("5")
Log.d("Error", "This document does not exist")
}
}else {
println("6")
task.exception?.message?.let {
Log.d(TAG, it)
}
}
println("7")
}
println("8")
}
Console error
The error is given because later I need to acces to userName var that is supposed to be filled in that function
To be able to get user data, you have to create a reference that points to that document, perform a get() call and attach a listener, as seen in the following lines of code:
val db = FirebaseFirestore.getInstance()
val usersRef = db.collection("users")
usersRef.document("gA4z1AhkQpQ6J47sIMmCGIZRKDK2").get().addOnCompleteListener { task ->
if (task.isSuccessful) {
val document = task.result
if (document.exists()) {
val email = document.getString("email")
val pass = document.getString("pass")
val user = document.getString("user")
Log.d(TAG,"$email/$pass/$user")
} else {
Log.d(TAG, "The document doesn't exist.")
}
} else {
task.exception?.message?.let {
Log.d(TAG, it)
}
}
}
The result in the logcat will be:
barrooroor#gmail.com/paport/do3fe4232ef2
If "gA4z1AhkQpQ6J47sIMmCGIZRKDK2" is the ID of the user that comes from the authentication process, then instead of the hard coded ID, you can simply use:
val auth = FirebaseAuth.getInstance()
val uid = auth.currentUser?.uid
usersRef.document(uid).get().addOnCompleteListener {/* ... /*}
// 👆
Besides that, something more important, never store sensitive data as passwords in plain text. Malicious users might take advantage of that. Always use Firebase Authentication for that and secure the database using Firestore Security Rules.
I am trying to add google play services in my android game. I have followed all the steps shown in https://developers.google.com/games/services/console/enabling this link.
I have added the app id in AndroidManifest.xml file.
Then, I followed the steps shown in this https://developers.google.com/games/services/android/signin link.
I am still getting this exception "com.google.android.gms.common.api.ApiException: 4: 4:" everytime. I guess the status code I am getting as a result is SIGN_IN_REQUIRED.
Do I have to release it for testing on the google play console. Can not I just test it by running on a physical device by using android studio?
class MainActivity : AppCompatActivity() {
var googleLogInResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
// There are no request codes
val result = Auth.GoogleSignInApi.getSignInResultFromIntent(result.data)
if (result.isSuccess) {
// The signed in account is stored in the result.
val signedInAccount = result.signInAccount
} else {
var message = result.status.statusMessage
if (message == null || message.isEmpty()) {
message = "signin_other_error"
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN
)
}
private fun signInSilently() {
val signInOptions = GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN
val account = GoogleSignIn.getLastSignedInAccount(this)
if (GoogleSignIn.hasPermissions(account, *signInOptions.scopeArray)) {
// Already signed in.
// The signed in account is stored in the 'account' variable.
val signedInAccount = account
Log.d("MainActivity", "Account is already signed in")
} else {
// Haven't been signed-in before. Try the silent sign-in first.
val signInClient = GoogleSignIn.getClient(this, signInOptions)
signInClient
.silentSignIn()
.addOnCompleteListener(
this
) { task ->
if (task.isSuccessful) {
// The signed in account is stored in the task's result.
val signedInAccount = task.result
Log.d("MainActivity", "sign in successful")
} else {
// Player will need to sign-in explicitly using via UI.
// See [sign-in best practices](http://developers.google.com/games/services/checklist) for guidance on how and when to implement Interactive Sign-in,
// and [Performing Interactive Sign-in](http://developers.google.com/games/services/android/signin#performing_interactive_sign-in) for details on how to implement
// Interactive Sign-in.
startSignInIntent()
/*Log.d("dsds", "sign in failed: " + (task.exception as ApiException).statusCode)*/
Log.d("MainActivity", "sign in failed: " + task.exception)
}
}
}
}
private fun startSignInIntent() {
val signInClient = GoogleSignIn.getClient(
this,
GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN
)
val intent = signInClient.signInIntent
googleLogInResult.launch(intent)
}
override fun onResume() {
super.onResume()
signInSilently()
}
}
Finally, I found my problem. I was logged in from different google account and it was not my tester account.
I am building an android app in MVVM architecture using Firebase. I am trying to do User's password change and whenever i start my code, application freezes or just stops responding. I spent a lot of time to search what is wrong with it and yet no fix. If anyone know why it behave like this I would appreciate your help. My code:
Function called in fragment:
private fun startChangePasswordDialog(){
val dialogView = LayoutInflater.from(activity).inflate(R.layout.dialog_change_password, null)
val builder = AlertDialog.Builder(activity).setView(dialogView)
val dialog: AlertDialog = builder.show()
val changePassword = dialogView.findViewById<Button>(R.id.changePasswordBT)
val cancel = dialogView.findViewById<Button>(R.id.changePasswordCancelBT)
val passwordET = dialogView.findViewById<EditText>(R.id.changePasswordET)
changePassword?.setOnClickListener {
val newPassword = passwordET.text.trim().toString()
if (TextUtils.isEmpty(newPassword) || newPassword.length < viewModel.PASSWORD_MIN_VALUE){
Toast.makeText(requireContext(), R.string.password_too_short, Toast.LENGTH_SHORT).show()
}
else{
viewModel.changeUsersPassword(newPassword)
viewModel.needUserAuthentication.observe(requireActivity(), {
if (it == true) reAuthenticateUser()
})
}
dialog.dismiss()
}
cancel?.setOnClickListener {
dialog.dismiss()
}
ViewModel function:
fun changeUsersPassword(password: String) {
Log.d(TAG,"Starting user's password change procedure")
when (repository.changeUserPassword(password)){
PasswordChangeCallbackEnum.FACEBOOK_USER -> {
_toastMessage.value = R.string.facebook_user_password_change
Log.d(TAG, "User's password will not be changed, logged in as Facebook user")
}
PasswordChangeCallbackEnum.PASSWORD_CHANGE_ERROR -> {
_toastMessage.value = R.string.password_change_error
Log.d(TAG, "Error while changing user's password")
}
PasswordChangeCallbackEnum.PASSWORD_CHANGED -> {
_toastMessage.value = R.string.password_change_success
Log.d(TAG, "User's password changed successfully")
}
PasswordChangeCallbackEnum.NEED_USER_AUTHENTICATION -> {
_needUserAuthentication.value = true
}
}
}
Firebase Repository (I have changed it several times when tried to fix this):
fun changeUserPassword(password: String): PasswordChangeCallbackEnum {
var result = PasswordChangeCallbackEnum.PASSWORD_CHANGE_ERROR
if (currentUser != null) {
for (userInfo in currentUser.providerData) {
if (userInfo.providerId == "facebook.com") {
Log.d(TAG, "Cannot change password for user logged in with facebook")
result = PasswordChangeCallbackEnum.FACEBOOK_USER
}
}
}
try{
val updateTask = authentication.currentUser?.updatePassword(password)
updateTask?.addOnSuccessListener {
Log.d(TAG, "User's password change state: SUCCESS")
result = PasswordChangeCallbackEnum.PASSWORD_CHANGED
}
}catch (exception: FirebaseAuthRecentLoginRequiredException){
Log.d(TAG, "Need user to authenticate again")
result = PasswordChangeCallbackEnum.NEED_USER_AUTHENTICATION
}
return result
}
The problem is, you are doing task in ui thread.
Use coroutines for the task to do in worker thread .
you can have more information about coroutines. here
You can also use RxJava for it or some Async task.
It will prevent the ui freezing
I am trying to write some basic information about users to a Firebase realtime database, but when running setValue() nothing happens in the console and after many hours of trying I still can't figure out the problem.
This is the code in the fragment that calls the registerUser function:
registerButton.setOnClickListener {
firstNameLayout.error = null
lastNameLayout.error = null
emailFieldLayout.error = null
passwordFieldLayout.error = null
var fieldCheck = true
if(TextUtils.isEmpty(firstNameField.text)) {
firstNameLayout.error = "Please Enter First Name";
fieldCheck = false
}
if (TextUtils.isEmpty(lastNameField.text)) {
lastNameLayout.error = "Please Enter Last Name"
fieldCheck = false
}
if(TextUtils.isEmpty(emailField.text)) {
emailFieldLayout.error = "Please Enter Email Address";
fieldCheck = false
// Todo: Make sure format is correct
}
if(TextUtils.isEmpty(passwordField.text)){
passwordFieldLayout.error = "Please Choose a Password"
fieldCheck = false
// Todo: Stricter password requirements
}
if(!fieldCheck) {
return#setOnClickListener
}
registerButton.startAnimation()
val fName = firstNameField.text.toString().trim()
val lName = lastNameField.text.toString().trim()
val email = emailField.text.toString().trim()
val password = passwordField.text.toString().trim()
try {
registerButton.doneLoadingAnimation(2, bitmap)
(activity as LoginRegister).registerUser(email, password, fName, lName)
} catch (e: Exception) {
Toast.makeText(activity, e.message, Toast.LENGTH_SHORT).show()
registerButton.revertAnimation()
}
}
This is the registerUser function body from the parent activity of the fragment:
fun registerUser(email: String, password: String, fName: String, lName: String) {
//Registrerer med Firebase Authentication
auth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "createUserWithEmail:success")
val user : FirebaseUser = task.result!!.user!!
val uid: String = user.uid
val dbUser = User(fName, lName, email)
writeNewUser(uid, dbUser)
val intent = Intent(this#LoginRegister, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
intent.putExtra("user_email", email)
intent.putExtra("user_first_name", fName)
intent.putExtra("user_last_name", lName)
startActivity(intent)
finish()
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "createUserWithEmail:failure", task.exception)
Toast.makeText(baseContext, "Authentication failed.", Toast.LENGTH_SHORT).show()
}
}
}
This is the writeNewUser function body
fun writeNewUser(userId: String, user: User) {
database = FirebaseDatabase.getInstance().getReference("Users").child(userId)
database.child("users").child(userId).setValue(user)
}
The User object is instantiated from this kotlin class:
data class User(val fName: String? = null, val lName: String? = null, val email: String? = null) {
val firstName = fName?.capitalize(Locale.ROOT)
val lastName = lName?.capitalize(Locale.ROOT)
val emailAddress = email
}
Firebase:
Firebase nodes
Anyone know the reason behind this?
put the URL of database in the getInstance() was the solution for me, now it works perfect
example:
private val database = FirebaseDatabase.getInstance("https://conect-c91b3-default-rtdb.firebaseio.com/").getReference()
// to test if is working
database.child("users").setValue("test")
I solved it, I was using another database location than the default, and I didn't catch the fact that I needed to pass the database URL in the getInstance() method. Passing the correct database url fixed the issue
fun writeNewUser(userId: String, user: User) {
database = FirebaseDatabase.getInstance().getReference("Users").child(userId)
database.child("users").child(userId).setValue(user)
}
I guess you duplicate a couple of child nodes in above snippet as it implies a group of users has a single user, and this particular user has a group of users, doesn't make sense, so you can remove these duplicates:
fun writeNewUser(userId: String, user: User) {
database = FirebaseDatabase.getInstance().getReference("Users").child(userId)
database.setValue(user)
}
UPDATE:
The firebase nodes are case-sensitive, so change "Users" to lowercase "users"
fun writeNewUser(userId: String, user: User) {
database = FirebaseDatabase.getInstance().getReference("users").child(userId)
database.setValue(user)
}
I am developing an Android app where all my users who logged inside my application should be remembered. More specifically, get their Device Key register in their user profile. The problem here is my currSignedDevice shows “null” as shown below and when I try to remember it, It is not being remembered.
I am using AWS Cognito and followed their documentation here https://aws.amazon.com/blogs/mobile/tracking-and-remembering-devices-using-amazon-cognito-your-user-pools/. I set my device settings in Cognito and implemented with code below.
fun getSignInUserDeviceDetails(user: String?):Boolean {
var currSignedDevice = userPool.getUser (user).thisDevice();
println("device is "+ currSignedDevice +"\t")
var changeDeviceSettingsHandler: GenericHandler = object : GenericHandler {
override fun onSuccess() {
// Device status successfully changed
println("device remembered successfully")
}
override fun onFailure(exception: java.lang.Exception) {
// Probe exception for the cause of the failure
println("failure in remember device")
}
}
currSignedDevice?.rememberThisDevice(changeDeviceSettingsHandler)
return true
}
Called this function in my loginfragment after the valid authentication.
login.setOnClickListener {
val email = email.text.toString()
val password = password.text.toString()
if (email.trim().isEmpty()) {
toastError("Enter an email.")
return#setOnClickListener
}
if (password.trim().isEmpty()) {
toastError("Enter a password.")
return#setOnClickListener
}
emailVal = email.toLowerCase()
passwordVal = password
signInDialog?.show()
activity?.let {
(it as MainActivity).setDialog50PercentWidth(signInDialog)
}
viewModel.authenticate(email, password, confirmUserHandler)
AWSClient.instance.getSignInUserDeviceDetails(emailVal)
}
The problem here is my currSignedDevice shows “null” as shown below in the figure and when I try to remember it, It is not being remembered. This is also not going inside of success or failure. It’s directly jumping out from that block.
fun getSignInUserDeviceDetails(user: String?):Boolean {
var currSignedDevice = userPool.getUser (user).thisDevice();
println("device is "+ currSignedDevice +"\t") // gives my currSignedDevice as NULL when I debug
var changeDeviceSettingsHandler: GenericHandler = object : GenericHandler {
override fun onSuccess() {
println("device remembered successfully")
}
override fun onFailure(exception: java.lang.Exception) {
// Probe exception for the cause of the failure
println("failure in remeber device")
}
}
currSignedDevice.rememberThisDevice(changeDeviceSettingsHandler)
return true
}