You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复安卓应用中Firebase OTP验证及短信权限问题?

Fixing Firebase OTP Auto-Retrieval After Removing SMS Permissions

Hey Krishna, I’ve run into this exact issue with clients before—Google’s permission restrictions can throw a wrench in OTP flows, but Firebase has an official, policy-compliant workaround: the SMS Retriever API. Let’s walk through how to implement it step by step so your app can auto-retrieve OTPs without needing READ_SMS or RECEIVE_SMS permissions.

Step 1: Add the SMS Retriever API Dependency

First, update your module-level build.gradle (or build.gradle.kts) to include the required libraries:

dependencies {
    // Firebase Phone Auth
    implementation "com.google.firebase:firebase-auth:22.3.0"
    // SMS Retriever API
    implementation "com.google.android.gms:play-services-auth-api-phone:18.0.1"
}

Sync your project after adding these to apply the changes.

Step 2: Generate Your App’s Signature Hash

The SMS Retriever API only listens for SMS messages that include a unique hash tied to your app’s signature and package name. To generate this hash:

  1. Create a helper class to fetch the signature hash:
import android.content.Context
import android.content.pm.PackageManager
import android.util.Base64
import android.util.Log
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException

class AppSignatureHelper(context: Context) {
    private val TAG = "AppSignatureHelper"
    private val appContext: Context = context.applicationContext

    fun getAppSignatures(): List<String> {
        val appSignatures = mutableListOf<String>()
        try {
            val packageName = appContext.packageName
            val packageInfo = appContext.packageManager.getPackageInfo(
                packageName,
                PackageManager.GET_SIGNATURES
            )
            for (signature in packageInfo.signatures) {
                val md = MessageDigest.getInstance("SHA-256")
                md.update(signature.toByteArray())
                val hash = Base64.encodeToString(md.digest(), Base64.NO_WRAP)
                appSignatures.add(hash)
                Log.d(TAG, "App signature hash: $hash")
            }
        } catch (e: PackageManager.NameNotFoundException) {
            Log.e(TAG, "Package not found", e)
        } catch (e: NoSuchAlgorithmException) {
            Log.e(TAG, "No such algorithm", e)
        }
        return appSignatures
    }
}
  1. Call this class from your activity to get the hash (run this once in debug mode to note the value):
val signatureHelper = AppSignatureHelper(this)
val signatures = signatureHelper.getAppSignatures()
// Check Logcat for your hash—copy this, you’ll need it for your SMS template

⚠️ Important: Use the hash generated with your release signing key for production builds. The debug hash won’t work with your published app.

Step 3: Set Up a BroadcastReceiver to Capture OTP

Create a receiver that listens for the SMS Retriever’s success intent:

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.google.android.gms.auth.api.phone.SmsRetriever
import com.google.android.gms.common.api.CommonStatusCodes
import com.google.android.gms.common.api.Status

class SmsBroadcastReceiver : BroadcastReceiver() {
    private var otpListener: OtpListener? = null

    fun setOtpListener(listener: OtpListener) {
        this.otpListener = listener
    }

    override fun onReceive(context: Context?, intent: Intent?) {
        if (SmsRetriever.SMS_RETRIEVED_ACTION == intent?.action) {
            val extras = intent.extras
            val status = extras?.get(SmsRetriever.EXTRA_STATUS) as Status

            when (status.statusCode) {
                CommonStatusCodes.SUCCESS -> {
                    // Get SMS message contents
                    val message = extras.get(SmsRetriever.EXTRA_SMS_MESSAGE) as String
                    // Extract OTP from the message (adjust regex based on your SMS template)
                    val otpPattern = "\\d{6}".toRegex() // Matches 6-digit OTP
                    val otpMatch = otpPattern.find(message)
                    otpMatch?.value?.let { otp ->
                        otpListener?.onOtpReceived(otp)
                    }
                }
                CommonStatusCodes.TIMEOUT -> {
                    // SMS not received within 5 minutes
                    otpListener?.onOtpTimeout()
                }
            }
        }
    }

    interface OtpListener {
        fun onOtpReceived(otp: String)
        fun onOtpTimeout()
    }
}

Step 4: Register the Receiver and Start SMS Retriever

In your activity where you handle OTP verification:

  1. Register the broadcast receiver (preferably in onStart()):
private lateinit var smsReceiver: SmsBroadcastReceiver

override fun onStart() {
    super.onStart()
    smsReceiver = SmsBroadcastReceiver()
    smsReceiver.setOtpListener(object : SmsBroadcastReceiver.OtpListener {
        override fun onOtpReceived(otp: String) {
            // Auto-fill the OTP input field or pass it to Firebase
            binding.otpEditText.setText(otp)
            verifyOtpWithFirebase(otp)
        }

        override fun onOtpTimeout() {
            // Show error to user
            Toast.makeText(this@OtpActivity, "OTP timed out. Please request a new one.", Toast.LENGTH_SHORT).show()
        }
    })
    val intentFilter = IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION)
    registerReceiver(smsReceiver, intentFilter)
}

override fun onStop() {
    super.onStop()
    unregisterReceiver(smsReceiver)
}
  1. Start the SMS Retriever before sending the OTP request to Firebase:
private fun startSmsRetriever() {
    val client = SmsRetriever.getClient(this)
    val task = client.startSmsRetriever()
    task.addOnSuccessListener {
        // Retriever started successfully—now send the OTP request to Firebase
        sendOtpToUser()
    }
    task.addOnFailureListener {
        // Failed to start retriever—handle error
        Toast.makeText(this, "Failed to start OTP retrieval", Toast.LENGTH_SHORT).show()
    }
}

private fun sendOtpToUser() {
    // Your existing Firebase Phone Auth code to send OTP
    val phoneNumber = binding.phoneEditText.text.toString()
    val options = PhoneAuthOptions.newBuilder(FirebaseAuth.getInstance())
        .setPhoneNumber(phoneNumber)
        .setTimeout(60L, TimeUnit.SECONDS)
        .setActivity(this)
        .setCallbacks(object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
            override fun onVerificationCompleted(credential: PhoneAuthCredential) {
                // Auto-verification completed (rare case)
                signInWithPhoneAuthCredential(credential)
            }

            override fun onVerificationFailed(e: FirebaseException) {
                // Handle verification failure
                Toast.makeText(this@OtpActivity, "Verification failed: ${e.message}", Toast.LENGTH_SHORT).show()
            }

            override fun onCodeSent(
                verificationId: String,
                token: PhoneAuthProvider.ForceResendingToken
            ) {
                // Save verification ID and resending token for later use
                storedVerificationId = verificationId
                resendToken = token
            }
        })
        .build()
    PhoneAuthProvider.verifyPhoneNumber(options)
}

Step 5: Update Your OTP SMS Template

Your OTP message must include your app’s signature hash at the end (no extra spaces). For example:

Your verification code is: 123456

ABC123xyz789

Replace ABC123xyz789 with the hash you generated earlier. If you’re using Firebase’s default SMS template, go to the Firebase Console → Authentication → Sign-in method → Phone → SMS template, and add the hash to the message.

Key Notes

  • No Permissions Needed: The SMS Retriever API doesn’t require READ_SMS or RECEIVE_SMS—it uses Google Play Services to safely retrieve the SMS without accessing your app’s SMS inbox.
  • Release Signing Hash: Always use the hash generated with your release key for production. If you use App Bundle, make sure to generate the hash from the upload key (not the app signing key if Google manages your signing).
  • OTP Extraction: Adjust the regex in SmsBroadcastReceiver to match your OTP length (e.g., \\d{4} for 4-digit codes).

Once you implement this flow, your app will auto-retrieve OTPs while staying compliant with Google’s privacy policies. Let me know if you hit any snags with specific steps!

内容的提问来源于stack exchange,提问作者Krishna Chhabria

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 06:49:16