이 문서에서는 Android 앱에 SMS 다단계 인증을 추가하는 방법을 설명합니다.
다단계 인증을 통해 앱의 보안이 강화됩니다. 공격자는 종종 비밀번호와 소셜 미디어 계정을 유출시키지만 문자 메시지를 가로채는 것은 더 어렵습니다.
시작하기 전에
다단계 인증을 지원하는 공급업체를 사용 설정합니다. 예를 들면 다음과 같습니다.
- 이메일 및 비밀번호
- 이메일 링크
- Google Play
- GitHub
- Microsoft
- Yahoo
앱이 사용자 이메일을 확인하고 있는지 확인합니다. MFA를 사용하려면 이메일 확인이 필요합니다. 이를 통해 악의적인 행위자가 자신이 소유하지 않은 이메일에 서비스를 등록한 후 두 번째 단계를 추가하여 실제 소유자의 접근을 막는 일을 방지할 수 있습니다.
Firebase Console에 앱의 SHA-1 해시를 등록합니다(변경사항은 자동으로 Google Cloud Identity Platform에 적용됩니다).
클라이언트 인증의 단계에 따라 앱의 SHA-1 해시를 가져옵니다.
Firebase Console을 엽니다.
프로젝트 설정으로 이동합니다.
내 앱에서 Android 아이콘을 클릭합니다.
안내에 따라 SHA-1 해시를 추가합니다.
다단계 인증 사용 설정
Cloud Console에서 Identity Platform MFA 페이지로 이동합니다.
MFA 페이지로 이동SMS 기반 다단계 인증 상자에서 사용 설정을 클릭합니다.
앱을 테스트할 전화번호를 입력합니다. 선택사항이지만 개발 중 제한이 발생하지 않도록 테스트 전화번호를 등록하는 것이 좋습니다.
아직 앱 도메인을 승인하지 않은 경우 오른쪽의 도메인 추가를 클릭하여 허용 목록에 도메인을 추가합니다.
저장을 클릭합니다.
등록 패턴 선택
앱에 다단계 인증이 필요한지 여부 및 사용자 등록 방법과 시기를 선택할 수 있습니다. 일반적인 패턴은 다음과 같습니다.
등록 시 사용자의 두 번째 단계를 등록합니다. 앱이 모든 사용자에게 다단계 인증을 요구한다면 이 방법을 사용하세요.
등록 시 두 번째 단계를 등록하는 건너뛸 수 있는 옵션을 제공하세요. 다단계 인증을 권고하지만 필수이지는 않은 앱은 이 방법을 사용하는 것이 좋습니다.
가입 화면이 아닌 사용자의 계정 또는 프로필 관리 페이지에서 두 번째 단계를 추가할 수 있도록 합니다. 이렇게 하면 등록 프로세스 중에 발생하는 마찰을 최소화하면서도 보안에 민감한 사용자에게 다단계 인증을 제공할 수 있습니다.
사용자가 보안 요구사항이 향상된 기능에 액세스하려고 할 때 두 번째 단계를 점진적으로 추가하도록 합니다.
두 번째 단계 등록
사용자의 새로운 두 번째 단계를 등록하려면 다음 단계를 따르세요.
사용자를 다시 인증합니다.
사용자에게 전화번호를 입력하도록 요청합니다.
사용자를 위한 다단계 세션을 가져옵니다.
user.getMultiFactor().getSession() .addOnCompleteListener( new OnCompleteListener<MultiFactorSession>() { @Override public void onComplete(@NonNull Task<MultiFactorSession> task) { if (task.isSuccessful()) { MultiFactorSession multiFactorSession = task.getResult(); } } });
인증 프로세스의 여러 이벤트를 처리하는
OnVerificationStateChangedCallbacks
객체를 빌드합니다.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
객체를 초기화합니다.PhoneAuthOptions phoneAuthOptions = PhoneAuthOptions.newBuilder() .setPhoneNumber(phoneNumber) .setTimeout(30L, TimeUnit.SECONDS) .setMultiFactorSession(MultiFactorSession) .setCallbacks(callbacks) .build();
기본적으로 즉각적인 인증이 사용 설정되어 있습니다. 사용 중지하려면
requireSmsValidation(true)
에 대한 호출을 추가합니다.사용자 전화로 인증 메시지를 전송합니다.
PhoneAuthProvider.verifyPhoneNumber(phoneAuthOptions);
필수 사항은 아니지만, 사용자에게 SMS 메시지가 전송되고 표준 요금이 적용된다는 점을 미리 알리는 것이 좋습니다.
SMS 코드가 전송되면 사용자에게 코드를 확인하도록 요청합니다.
// Ask user for the verification code. PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, verificationCode);
PhoneAuthCredential
로MultiFactorAssertion
객체를 초기화합니다.MultiFactorAssertion multiFactorAssertion = PhoneMultiFactorGenerator.getAssertion(credential);
등록을 완료합니다. 선택사항으로 두 번째 단계의 표시 이름을 지정할 수 있습니다. 인증 과정에서 전화번호가 마스킹되므로(예: +1******1234) 두 번째 단계가 여러 개인 사용자에게 유용합니다.
// 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) { // ... } });
다음 코드는 두 번째 단계를 등록하는 전체 예시를 보여줍니다.
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) {
// ...
}
});
수고하셨습니다. 사용자의 두 번째 인증 단계가 성공적으로 등록되었습니다.
두 번째 단계 인증으로 사용자 로그인 처리
2단계 SMS 인증으로 사용자를 로그인 처리하려면 다음 안내를 따르세요.
첫 번째 단계로 사용자를 로그인한 다음
FirebaseAuthMultiFactorException
예외를 포착합니다. 이 오류에는 사용자의 등록된 두 번째 단계를 가져오는 데 사용할 수 있는 리졸버가 포함됩니다. 첫 번째 단계로 사용자를 성공적으로 인증했음을 나타내는 기본 세션도 포함됩니다.예를 들어 사용자의 첫 번째 단계가 이메일과 비밀번호인 경우는 다음과 같습니다.
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()
호출 후 오류를 포착합니다.사용자가 보조 단계를 여러 개 등록한 경우 어떤 단계를 사용할지 질문합니다.
// 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() == PhoneMultiFactorGenerator.FACTOR_ID) { // User selected a phone second factor. MultiFactorInfo selectedHint = multiFactorResolver.getHints().get(selectedIndex); } else { // Unsupported second factor. // Note that only phone second factors are currently supported. }
힌트 및 다단계 세션을 사용하여
PhoneAuthOptions
객체를 초기화합니다. 이러한 값은FirebaseAuthMultiFactorException
에 연결된 리졸버에 포함됩니다.PhoneAuthOptions phoneAuthOptions = PhoneAuthOptions.newBuilder() .setMultiFactorHint(selectedHint) .setTimeout(30L, TimeUnit.SECONDS) .setMultiFactorSession(multiFactorResolver.getSession()) .setCallbacks(callbacks) // Optionally disable instant verification. // .requireSmsValidation(true) .build();
사용자 전화로 인증 메시지를 전송합니다.
// Send SMS verification code PhoneAuthProvider.verifyPhoneNumber(phoneAuthOptions);
SMS 코드가 전송되면 사용자에게 코드를 확인하도록 요청합니다.
// Ask user for the verification code. PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, verificationCode);
PhoneAuthCredential
로MultiFactorAssertion
객체를 초기화합니다.MultiFactorAssertion multiFactorAssertion = PhoneMultiFactorGenerator.getAssertion(credential);
resolver.resolveSignIn()
을 호출하여 2차 인증을 완료합니다. 그런 다음 표준 공급업체별 데이터 및 인증 사용자 인증 정보가 포함된 원래 로그인 결과에 액세스할 수 있습니다.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. } } });
아래 코드는 다단계 사용자를 위한 전체 로그인 예시를 보여줍니다.
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(selectedtHint)
.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를 사용하여 프로그래매틱 방식으로 다단계 사용자를 관리합니다.