Android 앱에서 SMS 인증 요청하기

전화번호를 자동으로 인증하려면 인증 절차에서 클라이언트와 서버 부분을 모두 구현해야 합니다. 이 문서에서는 Android 앱에서 클라이언트 부분을 구현하는 방법을 설명합니다.

Android 앱에서 전화번호 인증 흐름을 시작하려면 전화번호를 인증 서버로 전송하고 SMS Retriever API를 호출하여 앱의 일회성 코드가 포함된 SMS 메시지를 리슨합니다. 메시지를 받은 후에는 서버에 일회용 코드를 다시 전송하여 인증 절차를 완료합니다.

시작하기 전에

앱을 준비하려면 다음 섹션의 단계를 완료합니다.

앱 기본 요건

앱의 빌드 파일이 다음 값을 사용하는지 확인합니다.

  • 19 이상의 minSdkVersion
  • compileSdkVersion 28 또는 이후 버전

앱 구성

프로젝트 수준의 build.gradle 파일에서 buildscriptallprojects 섹션에 Google의 Maven 저장소Maven 중앙 저장소를 포함합니다.

buildscript {
    repositories {
        google()
        mavenCentral()
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()
    }
}

모듈의 Gradle 빌드 파일(일반적으로 app/build.gradle)에 SMS Retriever API의 Google Play 서비스 종속 항목을 추가합니다.

dependencies {
  implementation 'com.google.android.gms:play-services-auth:20.7.0'
  implementation 'com.google.android.gms:play-services-auth-api-phone:18.0.1'
}

1. 사용자의 전화번호 가져오기

앱에 적합한 방식으로 사용자의 전화번호를 가져올 수 있습니다. 종종 힌트 선택 도구를 사용하여 사용자에게 기기에 저장된 전화번호 중에서 선택하라는 메시지를 표시하는 것이 좋습니다. 따라서 전화번호를 직접 입력하지 않아도 됩니다. 힌트 선택 도구를 사용하려면 다음 단계를 따르세요.

// Construct a request for phone numbers and show the picker
private void requestHint() {
    HintRequest hintRequest = new HintRequest.Builder()
           .setPhoneNumberIdentifierSupported(true)
           .build();

    PendingIntent intent = Auth.CredentialsApi.getHintPickerIntent(
            apiClient, hintRequest);
    startIntentSenderForResult(intent.getIntentSender(),
            RESOLVE_HINT, null, 0, 0, 0);
}

// Obtain the phone number from the result
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
  if (requestCode == RESOLVE_HINT) {
      if (resultCode == RESULT_OK) {
          Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY);
          // credential.getId();  <-- will need to process phone number string
      }
  }
}

2 SMS 리트리버 시작

사용자의 전화번호를 확인할 준비가 되면 SmsRetrieverClient 객체의 인스턴스를 가져와 startSmsRetriever를 호출하고 성공 및 실패 리스너를 SMS 검색 작업에 연결합니다.

// Get an instance of SmsRetrieverClient, used to start listening for a matching
// SMS message.
SmsRetrieverClient client = SmsRetriever.getClient(this /* context */);

// Starts SmsRetriever, which waits for ONE matching SMS message until timeout
// (5 minutes). The matching SMS message will be sent via a Broadcast Intent with
// action SmsRetriever#SMS_RETRIEVED_ACTION.
Task<Void> task = client.startSmsRetriever();

// Listen for success/failure of the start Task. If in a background thread, this
// can be made blocking using Tasks.await(task, [timeout]);
task.addOnSuccessListener(new OnSuccessListener<Void>() {
  @Override
  public void onSuccess(Void aVoid) {
    // Successfully started retriever, expect broadcast intent
    // ...
  }
});

task.addOnFailureListener(new OnFailureListener() {
  @Override
  public void onFailure(@NonNull Exception e) {
    // Failed to start retriever, inspect Exception for more details
    // ...
  }
});

SMS 검색 작업은 앱을 식별하는 고유 문자열이 포함된 SMS 메시지를 최대 5분 동안 수신 대기합니다.

3. 서버로 전화번호 전송

사용자의 전화번호를 가져오고 SMS 메시지를 리슨하기 시작한 후 일반적으로 HTTPS POST 요청을 사용하여 어떤 방법으로든 사용자의 전화번호를 확인 서버로 전송합니다.

서버에서 인증 메시지를 생성하여 SMS를 통해 지정한 전화번호로 전송합니다. 서버에서 SMS 인증 실행하기를 참고하세요.

4. 확인 메일 받기

사용자 기기에 확인 메시지가 수신되면 Play 서비스는 메시지 내용을 포함하는 SmsRetriever.SMS_RETRIEVED_ACTION 인텐트를 앱에 명시적으로 브로드캐스트합니다. 이 인증 메시지를 수신하려면 BroadcastReceiver를 사용합니다.

BroadcastReceiveronReceive 핸들러에서 인텐트 추가 항목의 확인 메시지 텍스트를 가져옵니다.

/**
 * BroadcastReceiver to wait for SMS messages. This can be registered either
 * in the AndroidManifest or at runtime.  Should filter Intents on
 * SmsRetriever.SMS_RETRIEVED_ACTION.
 */
public class MySMSBroadcastReceiver extends BroadcastReceiver {

  @Override
  public void onReceive(Context context, Intent intent) {
    if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
      Bundle extras = intent.getExtras();
      Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS);

      switch(status.getStatusCode()) {
        case CommonStatusCodes.SUCCESS:
          // Get SMS message contents
          String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
          // Extract one-time code from the message and complete verification
          // by sending the code back to your server.
          break;
        case CommonStatusCodes.TIMEOUT:
          // Waiting for SMS timed out (5 minutes)
          // Handle the error ...
          break;
      }
    }
  }
}

다음 예와 같이 앱의 AndroidManifest.xml 파일에 인텐트 필터 com.google.android.gms.auth.api.phone.SMS_RETRIEVED (SmsRetriever.SMS_RETRIEVED_ACTION 상수 값) 및 권한 com.google.android.gms.auth.api.phone.permission.SEND (SmsRetriever.SEND_PERMISSION 상수 값)을 사용하거나 동적으로 Context.registerReceiver를 사용하여 이 BroadcastReceiver를 등록합니다.

<receiver android:name=".MySMSBroadcastReceiver" android:exported="true"
          android:permission="com.google.android.gms.auth.api.phone.permission.SEND">
    <intent-filter>
        <action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED"/>
    </intent-filter>
</receiver>

5. 인증 메시지에서 서버로 전송된 일회용 코드를 전송합니다.

이제 인증 메시지의 텍스트를 가져왔으므로 정규 표현식 또는 다른 로직을 사용하여 메시지에서 일회성 코드를 가져옵니다. 일회용 코드의 형식은 서버에서 코드를 구현한 방법에 따라 다릅니다.

마지막으로 보안 연결을 통해 서버에 일회용 코드를 보냅니다. 서버가 일회용 코드를 수신하면 전화번호가 인증되었음을 기록합니다.

선택사항: 비밀번호 대용 Smart Lock으로 전화번호 저장

(선택사항) 사용자가 전화번호를 인증한 후 전화번호를 다시 입력하거나 선택하지 않고도 다른 앱과 다른 기기에서 자동으로 사용할 수 있도록 비밀번호 대용 Smart Lock을 사용하여 이 전화번호 계정을 저장하라는 메시지를 사용자에게 표시할 수 있습니다.

Credential credential = new Credential.Builder(phoneNumberString)
        .setAccountType("https://signin.example.com")  // a URL specific to the app
        .setName(displayName)  // optional: a display name if available
        .build();
Auth.CredentialsApi.save(apiClient, credential).setResultCallback(
            new ResultCallback() {
                public void onResult(Result result) {
                    Status status = result.getStatus();
                    if (status.isSuccess()) {
                        Log.d(TAG, "SAVE: OK");  // already saved
                    } else if (status.hasResolution()) {
                        // Prompt the user to save
                        status.startResolutionForResult(this, RC_SAVE);
                    }
                }
            });

그런 다음 사용자가 앱을 재설치하거나 새 기기에 앱을 설치한 후 사용자에게 전화번호를 다시 요청하지 않고도 저장된 전화번호를 검색할 수 있습니다.

// On the next install, retrieve the phone number
mCredentialRequest = new CredentialRequest.Builder()
    .setAccountTypes("https://signin.example.com")  // the URL specific to the developer
    .build();
Auth.CredentialsApi.request(apiClient, mCredentialRequest).setResultCallback(
    new ResultCallback<CredentialRequestResult>() {
        public void onResult(CredentialRequestResult credentialRequestResult) {
            if (credentialRequestResult.getStatus().isSuccess()) {
                credentialRequestResult.getCredential().getId();  // this is the phone number
            }
        }
    });

// Then, initiate verification and sign the user in (same as original verification logic)