Android Firebase注册功能OTP验证失败问题求助
问题:OTP验证失效,Firebase发送正常但验证失败
问题详情
- 开发场景:学校小组项目的注册页面,使用Firebase Auth实现手机号OTP验证
- 异常现象:Firebase能正常发送OTP,但手动输入OTP后验证操作失败;移除OTP功能后,Realtime Database插入用户信息、Storage创建用户文件夹的功能完全正常
- 已排查操作:重建项目、检查依赖配置、使用配对真机运行、添加AppCheck,问题仍未解决
问题根源分析
查看代码后发现核心问题:从RegistrationActivity跳转到PhoneVerificationActivity时,未传递sourceActivity参数,导致PhoneVerificationActivity验证成功后无法触发返回RegistrationActivity的逻辑,最终RegistrationActivity收到RESULT_CANCELED,判定验证失败。
具体逻辑矛盾点:
RegistrationActivity的initiatePhoneVerification()方法中,仅传递了phoneNumber参数,未添加sourceActivity的Extra标识PhoneVerificationActivity的signInWithPhoneAuthCredential()方法中,需要判断sourceActivity是否为"RegistrationActivity"才会返回RESULT_OK,否则不会执行该分支逻辑
修复步骤
1. 修改RegistrationActivity的跳转逻辑
在initiatePhoneVerification()方法中添加sourceActivity参数:
private void initiatePhoneVerification() { String phoneNumber = phoneNumberEditText.getText().toString(); Intent intent = new Intent(this, PhoneVerificationActivity.class); intent.putExtra("phoneNumber", phoneNumber); // 添加来源页面标识,让验证页面知道当前是注册流程 intent.putExtra("sourceActivity", "RegistrationActivity"); startActivityForResult(intent, PHONE_VERIFICATION_REQUEST); }
2. 补充潜在检查点
- 确认Firebase控制台中Phone Auth已启用,并且添加了正确的SHA-1/SHA-256指纹(调试和发布版本都需要配置)
- 检查
PhoneVerificationActivity布局文件中,OTP输入框的ID是否与代码中的editTextCode一致 - 验证手机号格式:
RegistrationActivity中处理后的手机号(如+63开头)需和Firebase发送验证码的手机号完全一致,避免格式不匹配导致验证失败
相关源码
RegistrationActivity 源码
package com.example.best_csportal; import android.content.Intent; import android.os.Bundle; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; import com.google.firebase.database.DataSnapshot; import com.google.firebase.database.ValueEventListener; import com.google.firebase.database.DatabaseError; import com.google.firebase.storage.FirebaseStorage; import com.google.firebase.storage.StorageReference; public class RegistrationActivity extends AppCompatActivity { private static final int PHONE_VERIFICATION_REQUEST = 101; private EditText usernameEditText; private EditText phoneNumberEditText; private EditText passwordEditText; private EditText confirmPasswordEditText; private Button registerButton; private Button loginButton; private DatabaseReference databaseReference; private FirebaseAuth auth; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_registration); // Initialize Firebase Auth auth = FirebaseAuth.getInstance(); FirebaseDatabase database = FirebaseDatabase.getInstance("https://best-cs-default-rtdb.asia-southeast1.firebasedatabase.app"); databaseReference = database.getReference("users"); usernameEditText = findViewById(R.id.etext_rusername); phoneNumberEditText = findViewById(R.id.etext_rphone); passwordEditText = findViewById(R.id.etext_rpassword); confirmPasswordEditText = findViewById(R.id.etext_rcpassword); registerButton = findViewById(R.id.button_rregister); loginButton = findViewById(R.id.button_rlogin); registerButton.setOnClickListener(v -> initiatePhoneVerification()); loginButton.setOnClickListener(v -> startActivity(new Intent(this, LoginActivity.class))); } private void initiatePhoneVerification() { String phoneNumber = phoneNumberEditText.getText().toString(); Intent intent = new Intent(this, PhoneVerificationActivity.class); intent.putExtra("phoneNumber", phoneNumber); startActivityForResult(intent, PHONE_VERIFICATION_REQUEST); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == PHONE_VERIFICATION_REQUEST && resultCode == RESULT_OK) { registerUser(); } else { Toast.makeText(this, "OTP verification failed. Please try again.", Toast.LENGTH_LONG).show(); } } private void registerUser() { String username = usernameEditText.getText().toString().trim(); String rawPhoneNumber = phoneNumberEditText.getText().toString().trim(); String password = passwordEditText.getText().toString().trim(); String confirmPassword = confirmPasswordEditText.getText().toString().trim(); // Process the phone number for the desired format final String phoneNumber; if (rawPhoneNumber.startsWith("0")) { phoneNumber = rawPhoneNumber.replaceFirst("0", "+63"); } else if (!rawPhoneNumber.startsWith("+63")) { phoneNumber = "+63" + rawPhoneNumber; } else { phoneNumber = rawPhoneNumber; } if (username.isEmpty() || phoneNumber.isEmpty() || password.isEmpty() || confirmPassword.isEmpty()) { Toast.makeText(this, "All fields must be filled", Toast.LENGTH_SHORT).show(); return; } if (!password.equals(confirmPassword)) { Toast.makeText(this, "Passwords do not match", Toast.LENGTH_SHORT).show(); return; } // Check for duplicate usernames databaseReference.orderByChild("username").equalTo(username).addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { if (dataSnapshot.exists()) { Toast.makeText(RegistrationActivity.this, "Username already exists", Toast.LENGTH_SHORT).show(); } else { // Check for duplicate phone numbers databaseReference.orderByChild("phoneNumber").equalTo(phoneNumber).addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot phoneSnapshot) { if (phoneSnapshot.exists()) { Toast.makeText(RegistrationActivity.this, "Phone number already used", Toast.LENGTH_SHORT).show(); } else { // Register user in the Realtime Database String userId = databaseReference.push().getKey(); String folderPath = "users/" + userId + "/"; User user = new User(username, phoneNumber, password, folderPath); databaseReference.child(userId).setValue(user) .addOnCompleteListener(dbTask -> { if (dbTask.isSuccessful()) { createUserFolder(userId); Toast.makeText(RegistrationActivity.this, "Registration Successful", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(RegistrationActivity.this, "Registration Failed: " + dbTask.getException().getMessage(), Toast.LENGTH_SHORT).show(); } }); } } @Override public void onCancelled(DatabaseError databaseError) { Toast.makeText(RegistrationActivity.this, "Error: " + databaseError.getMessage(), Toast.LENGTH_SHORT).show(); } }); } } @Override public void onCancelled(DatabaseError databaseError) { Toast.makeText(RegistrationActivity.this, "Error: " + databaseError.getMessage(), Toast.LENGTH_SHORT).show(); } }); } private void createUserFolder(String userId) { FirebaseStorage storage = FirebaseStorage.getInstance(); StorageReference storageRef = storage.getReference(); StorageReference userFolder = storageRef.child("users/" + userId + "/"); // Create a dummy file to initialize the folder StorageReference dummyFile = userFolder.child("dummy.txt"); byte[] data = "This is a placeholder to create the folder".getBytes(); dummyFile.putBytes(data).addOnSuccessListener(aVoid -> { // Successfully created folder, now update the database databaseReference.child(userId).child("storagePath").setValue(userFolder.getPath()) .addOnFailureListener(e -> Toast.makeText(RegistrationActivity.this, "Failed to save storage path: " + e.getMessage(), Toast.LENGTH_SHORT).show()); }).addOnFailureListener(exception -> { // Handle unsuccessful uploads Toast.makeText(RegistrationActivity.this, "Failed to create user folder: " + exception.getMessage(), Toast.LENGTH_SHORT).show(); }); } }
PhoneVerificationActivity 源码
package com.example.best_csportal; import android.content.Intent; import android.os.Bundle; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.PhoneAuthCredential; import com.google.firebase.auth.PhoneAuthProvider; import java.util.concurrent.TimeUnit; import com.google.firebase.FirebaseException; import android.util.Log; import android.app.Activity; public class PhoneVerificationActivity extends AppCompatActivity { private EditText editTextCode; private Button buttonVerify; private FirebaseAuth mAuth; private String phoneNumber; private String verificationId; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.phoneverification_activity); mAuth = FirebaseAuth.getInstance(); editTextCode = findViewById(R.id.editTextCode); buttonVerify = findViewById(R.id.buttonVerify); // Retrieve the phone number passed from LoginActivity phoneNumber = getIntent().getStringExtra("phoneNumber"); sendVerificationCodeToUser(phoneNumber); buttonVerify.setOnClickListener(v -> { String code = editTextCode.getText().toString().trim(); if (!code.isEmpty()) { verifyVerificationCode(code); } }); } private void sendVerificationCodeToUser(String phoneNumber) { PhoneAuthProvider.getInstance().verifyPhoneNumber( phoneNumber, // Phone number to verify 60, // Timeout duration TimeUnit.SECONDS, // Unit of timeout this, // Activity (for callback binding) new PhoneAuthProvider.OnVerificationStateChangedCallbacks() { @Override public void onVerificationCompleted(PhoneAuthCredential credential) { // Auto-verification logic here signInWithPhoneAuthCredential(credential); } @Override public void onVerificationFailed(FirebaseException e) { Log.e("PhoneVerification", "Verification failed: " + e.getMessage()); Toast.makeText(PhoneVerificationActivity.this, "Verification failed: " + e.getMessage(), Toast.LENGTH_SHORT).show(); } @Override public void onCodeSent(String verificationId, PhoneAuthProvider.ForceResendingToken token) { Log.d("PhoneVerification", "Code sent successfully"); PhoneVerificationActivity.this.verificationId = verificationId; // Save the verification ID for later use } } ); } private void verifyVerificationCode(String code) { PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code); signInWithPhoneAuthCredential(credential); } private void signInWithPhoneAuthCredential(PhoneAuthCredential credential) { mAuth.signInWithCredential(credential) .addOnCompleteListener(this, task -> { if (task.isSuccessful()) { String sourceActivity = getIntent().getStringExtra("sourceActivity"); if ("RegistrationActivity".equals(sourceActivity)) { Intent resultIntent = new Intent(); setResult(Activity.RESULT_OK, resultIntent); finish(); } else if ("LoginActivity".equals(sourceActivity)) { Intent intent = new Intent(PhoneVerificationActivity.this, UserMainActivity.class); startActivity(intent); finish(); } } else { Toast.makeText(PhoneVerificationActivity.this, "Verification failed: " + task.getException().getMessage(), Toast.LENGTH_LONG).show(); setResult(Activity.RESULT_CANCELED); finish(); } }); } }
内容的提问来源于stack exchange,提问作者Dingrat Kahlil John Roderick
相关产品推荐
相关产品推荐

