땀이 삐질삐질 나는 개발 일기

SMS Retriever API 사용 본문

개발 Tip

SMS Retriever API 사용

삐질 2019. 3. 12. 21:22

개요

  1.  2019 / 01 /  09 부터 구글 플레이 스토어상의 SMS 권한 정책이 변경

  2. 자동 인증을 위한 SMS Read, Write 권한 제거

  3. 자동 인증 기능 대안 API → SMS Retriever API

    Reference :  https://developers.google.com/identity/sms-retriever/verify


사용

SMS Retriever API 사용하여 문자 인증 순서

  • 사용자 전화번호 획득 → HintRequest & PendingIntent

  • SMS 검색기 시작 →  SmsRetrieverClient  & start() 

  • 서버에 인증 요청  →   Client to Server

  • 인증 번호 수신  →  지정된 Form 문자 
    <#> 내용 + HashCode(전체 HashCode의 앞 11글자) 
    EX) <#> Authorization Code : 3356 INRiCZwJsdf

  • 인증 및 서버 반환


      이때  HashCode 구성하는 방법

  • 앱의 공개 키 인증서를 소문자의 16 진수 문자열로 가져옵니다

  • 패키지 명과, 공백 하나, 얻은 공개키 문자열을 하나의 스트링으로 합친 후

  • SHA-256으로 암호화

  • 인증 후 서버 회신



 HashCode를 얻어오기 위한 Helper 클래스 

(단, 헬퍼 클래스는 상용 앱 내에 호출되지 않아야 하고, HashCode를 얻은 후 클래스를 제거하는 것을 권함)

public class AppSignatureHelper extends ContextWrapper {
    public static final String TAG = AppSignatureHelper.class.getSimpleName();

    private static final String HASH_TYPE = "SHA-256";
    public static final int NUM_HASHED_BYTES = 9;
    public static final int NUM_BASE64_CHAR = 11;

    public AppSignatureHelper(Context context) {
        super(context);
    }

    /**
     * Get all the app signatures for the current package
     * @return
     */
    public ArrayList<String> getAppSignatures() {
        ArrayList<String> appCodes = new ArrayList<>();

        try {
            // Get all package signatures for the current package
            String packageName = getPackageName();
            PackageManager packageManager = getPackageManager();
            Signature[] signatures = packageManager.getPackageInfo(packageName,
                    PackageManager.GET_SIGNATURES).signatures;

            // For each signature create a compatible hash
            for (Signature signature : signatures) {
                String hash = hash(packageName, signature.toCharsString());
                if (hash != null) {
                    appCodes.add(String.format("%s", hash));
                }
            }
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "Unable to find package to obtain hash.", e);
        }
        return appCodes;
    }

    private static String hash(String packageName, String signature) {
        String appInfo = packageName + " " + signature;
        try {
            MessageDigest messageDigest = MessageDigest.getInstance(HASH_TYPE);
            messageDigest.update(appInfo.getBytes(StandardCharsets.UTF_8));
            byte[] hashSignature = messageDigest.digest();

            // truncated into NUM_HASHED_BYTES
            hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES);
            // encode into Base64
            String base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING | Base64.NO_WRAP);
            base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR);

            Log.d(TAG, String.format("pkg: %s -- hash: %s", packageName, base64Hash));
            return base64Hash;
        } catch (NoSuchAlgorithmException e) {
            Log.e(TAG, "hash:NoSuchAlgorithm", e);
        }
        return null;
    }
}


GoogleClientServie 객체 init

    private GoogleApiClient mCredentialsApiClient;

    public void setGoogleServieClient() {
        mCredentialsApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .enableAutoManage(this, this)
                .addApi(Auth.CREDENTIALS_API)
                .build();
    }


각 전화번호 얻기 

    private static final int RESOLVE_HINT = 1000;

   // 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(
                mCredentialsApiClient, 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
            }
        }
    }


SMSClient 작성 

 //Set Start SmsRetrieverClient
    public void setSmsClient() {
        // 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
                // ...
            }
        });
    }


브로드 캐스트 리시버 

class SmsBrReceiver extends BroadcastReceiver {

        /* public void setTimeout() {
             h.postDelayed(r, MAX_TIMEOUT);
         }*/
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent == null) {
                return;
            }

            String action = intent.getAction();
            if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(action)) {
                Bundle extras = intent.getExtras();
                Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS);
                switch (status.getStatusCode()) {
                    case CommonStatusCodes.SUCCESS:
                        String smsMessage = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
                        break;
                    case CommonStatusCodes.TIMEOUT:
                        break;
                    default:
                        break;
                }
            }
        }
    }




Comments