如果您已升級至 Firebase Authentication with Identity Platform,即可新增簡訊多重驗證 連結你的 Android 應用程式
多重驗證可提高應用程式安全性。雖然攻擊者 經常會竊取密碼和社群媒體帳戶、攔截簡訊, 變得更困難
事前準備
請至少啟用一個支援多重驗證的供應商。 每個供應商都支援多重驗證,但電話驗證、匿名驗證和 Apple Game Center。
確認應用程式正在驗證使用者的電子郵件地址。MFA 需要電子郵件驗證。 這可防止惡意人士利用電子郵件註冊服務 成為非擁有人,進而鎖定真正的擁有者 。
在 Firebase 控制台中註冊應用程式的 SHA-1 雜湊 (變更後的 自動沿用到 Google Cloud Firebase)。
請按照 驗證用戶端 取得應用程式的 SHA-1 雜湊。
開啟 Firebase 控制台。
前往「專案設定」。
按一下「您的應用程式」下方的「Android」圖示。
請按照指示新增 SHA-1 雜湊。
啟用多重驗證
開啟「驗證 >登入方式 Firebase控制台中的頁面。
在「進階」部分中,啟用「簡訊多重驗證」。
請一併輸入要測試應用程式的電話號碼。 雖然非必要,但我們強烈建議您註冊測試電話號碼 避免開發過程中發生節流現象
如果您尚未授權應用程式的網域,請將網域新增至允許清單 [驗證] > 中的清單設定 Firebase控制台中的頁面。
選擇註冊模式
您可以選擇是否要讓應用程式需要多因素驗證,以及如何 以及註冊使用者的時間常見的模式包括:
註冊使用者的第二重驗證在註冊過程中。使用這份草稿 方法。
提供可略過的選項,讓使用者在註冊期間註冊第二個步驟。應用程式 希望 (但不需要) 多因素驗證 他們偏好使用這個方法。
讓使用者能夠在帳戶或設定檔中新增次要驗證條件 而非註冊畫面這可以大幅減少 同時在進行多因素驗證時 。
使用者要存取應用程式時,需要逐步新增第二個驗證步驟 強化安全性要求的功能
註冊次要驗證方式
如何為使用者註冊新的次要驗證條件:
重新驗證使用者。
請使用者輸入電話號碼。
為使用者取得多重要素工作階段:
Kotlin+KTX
user.multiFactor.session.addOnCompleteListener { task -> if (task.isSuccessful) { val multiFactorSession: MultiFactorSession? = task.result } }
Java
user.getMultiFactor().getSession() .addOnCompleteListener( new OnCompleteListener<MultiFactorSession>() { @Override public void onComplete(@NonNull Task<MultiFactorSession> task) { if (task.isSuccessful()) { MultiFactorSession multiFactorSession = task.getResult(); } } });
建構
OnVerificationStateChangedCallbacks
物件來處理各種差異 驗證程序中的事件數:Kotlin+KTX
val callbacks = object : OnVerificationStateChangedCallbacks() { override fun onVerificationCompleted(credential: PhoneAuthCredential) { // This callback will be invoked in two situations: // 1) Instant verification. In some cases, the phone number can be // instantly verified without needing to send or enter a verification // code. You can disable this feature by calling // PhoneAuthOptions.builder#requireSmsValidation(true) when building // the options to pass to PhoneAuthProvider#verifyPhoneNumber(). // 2) Auto-retrieval. On some devices, Google Play services can // automatically detect the incoming verification SMS and perform // verification without user action. this@MainActivity.credential = credential } override fun onVerificationFailed(e: FirebaseException) { // This callback is invoked in response to invalid requests for // verification, like an incorrect phone number. if (e is FirebaseAuthInvalidCredentialsException) { // Invalid request // ... } else if (e is FirebaseTooManyRequestsException) { // The SMS quota for the project has been exceeded // ... } // Show a message and update the UI // ... } override fun onCodeSent( verificationId: String, forceResendingToken: ForceResendingToken ) { // The SMS verification code has been sent to the provided phone number. // We now need to ask the user to enter the code and then construct a // credential by combining the code with a verification ID. // Save the verification ID and resending token for later use. this@MainActivity.verificationId = verificationId this@MainActivity.forceResendingToken = forceResendingToken // ... } }
Java
OnVerificationStateChangedCallbacks callbacks = new OnVerificationStateChangedCallbacks() { @Override public void onVerificationCompleted(PhoneAuthCredential credential) { // This callback will be invoked in two situations: // 1) Instant verification. In some cases, the phone number can be // instantly verified without needing to send or enter a verification // code. You can disable this feature by calling // PhoneAuthOptions.builder#requireSmsValidation(true) when building // the options to pass to PhoneAuthProvider#verifyPhoneNumber(). // 2) Auto-retrieval. On some devices, Google Play services can // automatically detect the incoming verification SMS and perform // verification without user action. this.credential = credential; } @Override public void onVerificationFailed(FirebaseException e) { // This callback is invoked in response to invalid requests for // verification, like an incorrect phone number. if (e instanceof FirebaseAuthInvalidCredentialsException) { // Invalid request // ... } else if (e instanceof FirebaseTooManyRequestsException) { // The SMS quota for the project has been exceeded // ... } // Show a message and update the UI // ... } @Override public void onCodeSent( String verificationId, PhoneAuthProvider.ForceResendingToken token) { // The SMS verification code has been sent to the provided phone number. // We now need to ask the user to enter the code and then construct a // credential by combining the code with a verification ID. // Save the verification ID and resending token for later use. this.verificationId = verificationId; this.forceResendingToken = token; // ... } };
使用使用者的電話號碼初始化
PhoneInfoOptions
物件。 以及您的回呼:Kotlin+KTX
val phoneAuthOptions = PhoneAuthOptions.newBuilder() .setPhoneNumber(phoneNumber) .setTimeout(30L, TimeUnit.SECONDS) .setMultiFactorSession(MultiFactorSession) .setCallbacks(callbacks) .build()
Java
PhoneAuthOptions phoneAuthOptions = PhoneAuthOptions.newBuilder() .setPhoneNumber(phoneNumber) .setTimeout(30L, TimeUnit.SECONDS) .setMultiFactorSession(multiFactorSession) .setCallbacks(callbacks) .build();
即時驗證功能預設為啟用。如要停用這項功能,請新增通話 至
requireSmsValidation(true)
。傳送驗證訊息到使用者的手機:
Kotlin+KTX
PhoneAuthProvider.verifyPhoneNumber(phoneAuthOptions)
Java
PhoneAuthProvider.verifyPhoneNumber(phoneAuthOptions);
雖然並非必要,但最佳做法是事先告知使用者 會收到簡訊,支付一般簡訊費用
讓系統傳送簡訊驗證碼後,要求使用者驗證驗證碼:
Kotlin+KTX
// Ask user for the verification code. val credential = PhoneAuthProvider.getCredential(verificationId, verificationCode)
Java
// Ask user for the verification code. PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, verificationCode);
使用
PhoneAuthCredential
初始化MultiFactorAssertion
物件:Kotlin+KTX
val multiFactorAssertion = PhoneMultiFactorGenerator.getAssertion(credential)
Java
MultiFactorAssertion multiFactorAssertion = PhoneMultiFactorGenerator.getAssertion(credential);
完成註冊程序。您也可以選擇為 第二個步驟這項功能適用於具有多項第二項因素的使用者 系統會在驗證流程中遮蓋電話號碼 (例如, 例如 +1******1234)。
Kotlin+KTX
// Complete enrollment. This will update the underlying tokens // and trigger ID token change listener. FirebaseAuth.getInstance() .currentUser ?.multiFactor ?.enroll(multiFactorAssertion, "My personal phone number") ?.addOnCompleteListener { // ... }
Java
// Complete enrollment. This will update the underlying tokens // and trigger ID token change listener. FirebaseAuth.getInstance() .getCurrentUser() .getMultiFactor() .enroll(multiFactorAssertion, "My personal phone number") .addOnCompleteListener( new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { // ... } });
以下程式碼顯示第二重驗證註冊的完整範例:
Kotlin+KTX
val multiFactorAssertion = PhoneMultiFactorGenerator.getAssertion(credential)
user.multiFactor.session
.addOnCompleteListener { task ->
if (task.isSuccessful) {
val multiFactorSession = task.result
val phoneAuthOptions = PhoneAuthOptions.newBuilder()
.setPhoneNumber(phoneNumber)
.setTimeout(30L, TimeUnit.SECONDS)
.setMultiFactorSession(multiFactorSession)
.setCallbacks(callbacks)
.build()
// Send SMS verification code.
PhoneAuthProvider.verifyPhoneNumber(phoneAuthOptions)
}
}
// Ask user for the verification code.
val credential = PhoneAuthProvider.getCredential(verificationId, verificationCode)
val multiFactorAssertion = PhoneMultiFactorGenerator.getAssertion(credential)
// Complete enrollment.
FirebaseAuth.getInstance()
.currentUser
?.multiFactor
?.enroll(multiFactorAssertion, "My personal phone number")
?.addOnCompleteListener {
// ...
}
Java
MultiFactorAssertion multiFactorAssertion = PhoneMultiFactorGenerator.getAssertion(credential);
user.getMultiFactor().getSession()
.addOnCompleteListener(
new OnCompleteListener<MultiFactorSession>() {
@Override
public void onComplete(@NonNull Task<MultiFactorSession> task) {
if (task.isSuccessful()) {
MultiFactorSession multiFactorSession = task.getResult();
PhoneAuthOptions phoneAuthOptions =
PhoneAuthOptions.newBuilder()
.setPhoneNumber(phoneNumber)
.setTimeout(30L, TimeUnit.SECONDS)
.setMultiFactorSession(multiFactorSession)
.setCallbacks(callbacks)
.build();
// Send SMS verification code.
PhoneAuthProvider.verifyPhoneNumber(phoneAuthOptions);
}
}
});
// Ask user for the verification code.
PhoneAuthCredential credential =
PhoneAuthProvider.getCredential(verificationId, verificationCode);
MultiFactorAssertion multiFactorAssertion = PhoneMultiFactorGenerator.getAssertion(credential);
// Complete enrollment.
FirebaseAuth.getInstance()
.getCurrentUser()
.getMultiFactor()
.enroll(multiFactorAssertion, "My personal phone number")
.addOnCompleteListener(
new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
// ...
}
});
恭喜!您已成功註冊以下項目的第二個驗證方式: 而非個別使用者的帳戶
透過次要驗證方式登入使用者
如何透過雙重簡訊驗證功能登入使用者帳戶:
透過第一個步驟登入使用者,然後擷取
FirebaseAuthMultiFactorException
例外狀況。這項錯誤含有 解析器,可用來取得使用者已註冊的雙重驗證。 其中也包含可證明使用者成功的基礎工作階段 透過第一個因素驗證舉例來說,如果使用者的第一個驗證方法是電子郵件地址和密碼:
Kotlin+KTX
FirebaseAuth.getInstance() .signInWithEmailAndPassword(email, password) .addOnCompleteListener( OnCompleteListener { task -> if (task.isSuccessful) { // User is not enrolled with a second factor and is successfully // signed in. // ... return@OnCompleteListener } if (task.exception is FirebaseAuthMultiFactorException) { // The user is a multi-factor user. Second factor challenge is // required. val multiFactorResolver = (task.exception as FirebaseAuthMultiFactorException).resolver // ... } else { // Handle other errors, such as wrong password. } })
Java
FirebaseAuth.getInstance() .signInWithEmailAndPassword(email, password) .addOnCompleteListener( new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { if (task.isSuccessful()) { // User is not enrolled with a second factor and is successfully // signed in. // ... return; } if (task.getException() instanceof FirebaseAuthMultiFactorException) { // The user is a multi-factor user. Second factor challenge is // required. MultiFactorResolver multiFactorResolver = task.getException().getResolver(); // ... } else { // Handle other errors such as wrong password. } } });
如果使用者的第一個因素是聯合提供者 (例如 OAuth) 呼叫
startActivityForSignInWithProvider()
後擷取錯誤。如果使用者已註冊多項次要驗證因素,請詢問他們哪個 使用:
Kotlin+KTX
// Ask user which second factor to use. // You can get the list of enrolled second factors using // multiFactorResolver.hints // Check the selected factor: if (multiFactorResolver.hints[selectedIndex].factorId === PhoneMultiFactorGenerator.FACTOR_ID ) { // User selected a phone second factor. val selectedHint = multiFactorResolver.hints[selectedIndex] as PhoneMultiFactorInfo } else if (multiFactorResolver.hints[selectedIndex].factorId === TotpMultiFactorGenerator.FACTOR_ID) { // User selected a TOTP second factor. } else { // Unsupported second factor. }
Java
// Ask user which second factor to use. // You can get the masked phone number using // resolver.getHints().get(selectedIndex).getPhoneNumber() // You can get the display name using // resolver.getHints().get(selectedIndex).getDisplayName() if ( resolver.getHints() .get(selectedIndex) .getFactorId() .equals( PhoneMultiFactorGenerator.FACTOR_ID ) ) { // User selected a phone second factor. MultiFactorInfo selectedHint = multiFactorResolver.getHints().get(selectedIndex); } else if ( resolver .getHints() .get(selectedIndex) .getFactorId() .equals(TotpMultiFactorGenerator.FACTOR_ID ) ) { // User selected a TOTP second factor. } else { // Unsupported second factor. }
使用提示和多重要素初始化
PhoneAuthOptions
物件 會很有幫助這些值包含在附加至FirebaseAuthMultiFactorException
。Kotlin+KTX
val phoneAuthOptions = PhoneAuthOptions.newBuilder() .setMultiFactorHint(selectedHint) .setTimeout(30L, TimeUnit.SECONDS) .setMultiFactorSession(multiFactorResolver.session) .setCallbacks(callbacks) // Optionally disable instant verification. // .requireSmsValidation(true) .build()
Java
PhoneAuthOptions phoneAuthOptions = PhoneAuthOptions.newBuilder() .setMultiFactorHint(selectedHint) .setTimeout(30L, TimeUnit.SECONDS) .setMultiFactorSession(multiFactorResolver.getSession()) .setCallbacks(callbacks) // Optionally disable instant verification. // .requireSmsValidation(true) .build();
傳送驗證訊息到使用者的手機:
Kotlin+KTX
// Send SMS verification code PhoneAuthProvider.verifyPhoneNumber(phoneAuthOptions)
Java
// Send SMS verification code PhoneAuthProvider.verifyPhoneNumber(phoneAuthOptions);
讓系統傳送簡訊驗證碼後,要求使用者驗證驗證碼:
Kotlin+KTX
// Ask user for the verification code. Then, pass it to getCredential: val credential = PhoneAuthProvider.getCredential(verificationId, verificationCode)
Java
// Ask user for the verification code. Then, pass it to getCredential: PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, verificationCode);
使用
PhoneAuthCredential
初始化MultiFactorAssertion
物件:Kotlin+KTX
val multiFactorAssertion = PhoneMultiFactorGenerator.getAssertion(credential)
Java
MultiFactorAssertion multiFactorAssertion = PhoneMultiFactorGenerator.getAssertion(credential);
請呼叫
resolver.resolveSignIn()
完成次要驗證。 接著,您可以存取原始登入結果,包括 標準供應商專屬資料和驗證憑證:Kotlin+KTX
multiFactorResolver .resolveSignIn(multiFactorAssertion) .addOnCompleteListener { task -> if (task.isSuccessful) { val authResult = task.result // AuthResult will also contain the user, additionalUserInfo, // and an optional credential (null for email/password) // associated with the first factor sign-in. // For example, if the user signed in with Google as a first // factor, authResult.getAdditionalUserInfo() will contain data // related to Google provider that the user signed in with; // authResult.getCredential() will contain the Google OAuth // credential; // authResult.getCredential().getAccessToken() will contain the // Google OAuth access token; // authResult.getCredential().getIdToken() contains the Google // OAuth ID token. } }
Java
multiFactorResolver .resolveSignIn(multiFactorAssertion) .addOnCompleteListener( new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { if (task.isSuccessful()) { AuthResult authResult = task.getResult(); // AuthResult will also contain the user, additionalUserInfo, // and an optional credential (null for email/password) // associated with the first factor sign-in. // For example, if the user signed in with Google as a first // factor, authResult.getAdditionalUserInfo() will contain data // related to Google provider that the user signed in with. // authResult.getCredential() will contain the Google OAuth // credential. // authResult.getCredential().getAccessToken() will contain the // Google OAuth access token. // authResult.getCredential().getIdToken() contains the Google // OAuth ID token. } } });
以下程式碼是多因素使用者登入的完整範例:
Kotlin+KTX
FirebaseAuth.getInstance()
.signInWithEmailAndPassword(email, password)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
// User is not enrolled with a second factor and is successfully
// signed in.
// ...
return@addOnCompleteListener
}
if (task.exception is FirebaseAuthMultiFactorException) {
val multiFactorResolver =
(task.exception as FirebaseAuthMultiFactorException).resolver
// Ask user which second factor to use. Then, get
// the selected hint:
val selectedHint =
multiFactorResolver.hints[selectedIndex] as PhoneMultiFactorInfo
// Send the SMS verification code.
PhoneAuthProvider.verifyPhoneNumber(
PhoneAuthOptions.newBuilder()
.setActivity(this)
.setMultiFactorSession(multiFactorResolver.session)
.setMultiFactorHint(selectedHint)
.setCallbacks(generateCallbacks())
.setTimeout(30L, TimeUnit.SECONDS)
.build()
)
// Ask user for the SMS verification code, then use it to get
// a PhoneAuthCredential:
val credential =
PhoneAuthProvider.getCredential(verificationId, verificationCode)
// Initialize a MultiFactorAssertion object with the
// PhoneAuthCredential.
val multiFactorAssertion: MultiFactorAssertion =
PhoneMultiFactorGenerator.getAssertion(credential)
// Complete sign-in.
multiFactorResolver
.resolveSignIn(multiFactorAssertion)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
// User successfully signed in with the
// second factor phone number.
}
// ...
}
} else {
// Handle other errors such as wrong password.
}
}
Java
FirebaseAuth.getInstance()
.signInWithEmailAndPassword(email, password)
.addOnCompleteListener(
new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// User is not enrolled with a second factor and is successfully
// signed in.
// ...
return;
}
if (task.getException() instanceof FirebaseAuthMultiFactorException) {
FirebaseAuthMultiFactorException e =
(FirebaseAuthMultiFactorException) task.getException();
MultiFactorResolver multiFactorResolver = e.getResolver();
// Ask user which second factor to use.
MultiFactorInfo selectedHint =
multiFactorResolver.getHints().get(selectedIndex);
// Send the SMS verification code.
PhoneAuthProvider.verifyPhoneNumber(
PhoneAuthOptions.newBuilder()
.setActivity(this)
.setMultiFactorSession(multiFactorResolver.getSession())
.setMultiFactorHint(selectedHint)
.setCallbacks(generateCallbacks())
.setTimeout(30L, TimeUnit.SECONDS)
.build());
// Ask user for the SMS verification code.
PhoneAuthCredential credential =
PhoneAuthProvider.getCredential(verificationId, verificationCode);
// Initialize a MultiFactorAssertion object with the
// PhoneAuthCredential.
MultiFactorAssertion multiFactorAssertion =
PhoneMultiFactorGenerator.getAssertion(credential);
// Complete sign-in.
multiFactorResolver
.resolveSignIn(multiFactorAssertion)
.addOnCompleteListener(
new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// User successfully signed in with the
// second factor phone number.
}
// ...
}
});
} else {
// Handle other errors such as wrong password.
}
}
});
恭喜!您已成功透過多重要素登入使用者 驗證。
後續步驟
- 管理多重驗證使用者 透過程式利用 Admin SDK。