본문 바로가기

android

SmsRetreiever 사용해서 인증번호 얻어오기

인증번호가 오고 자동으로 입력해주는 기능이 있는데 요즘 앱에서는 대부분 채용을 하는 것 같다.. ()

 

그래서 나도 해당 기능을 구현해볼려고 구글에서 검색을 해보고 직접 구현을 해 본 부분을 기록으로 남기고자 글을 적습니다.

 

구글 공식 문서

 

SMS Retriever API를 사용한 자동 SMS 인증  |  Credential Verification  |  Google Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. Switch to English 의견 보내기 SMS Retriever API를 사용한 자동 SMS 인증 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분

developers.google.com

 

우선 dependencies에 추가를 해준다.

 

그리고 manifests에 등록할 BroadcastReceiver를 만들어 준다.

 

class MySmsReceiver : BroadcastReceiver() {
    companion object {
        var smsBroadcastReceiverListener: SmsBroadcastReceiverListener? = null

        fun unBindListener() {
            smsBroadcastReceiverListener = null
        }

        fun bindListener(smsBroadcastReceiverListener: SmsBroadcastReceiverListener) {
            Companion.smsBroadcastReceiverListener = smsBroadcastReceiverListener
        }

    }


    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 -> {
                    var message = extras.getString(SmsRetriever.EXTRA_SMS_MESSAGE)
                    //todo 파싱을 해서 코드만 넘겨주도록 !

                    if (message != null) {
                        smsBroadcastReceiverListener?.onSuccess(message.trim { it <= ' ' })
                    }
                }

                CommonStatusCodes.TIMEOUT -> {
                    smsBroadcastReceiverListener?.onFailure()
                }

            }
        }

    }

}

 

그리고 manifests에서 application 태그 안에

 

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

</receiver>

해당 receiver를 등록해준다.

 

문자가 오면 이제 receiver가 해당 앱에서 캐치를 해야하는 문자인지 알 수 있게 하는 부분이 필요한데

이 때 11자리로 이루어진 hash를 쓰게 된다.

 

현재 근무하는 회사에서 오는 sms 양식은 이렇습니다.

 

[web발신]

인증번호 [ xxxxxx ] 을 입력해주세요.

hash11자리

 

대충 이런식으로 왔던 것 같다.

 

hash 11자리를 구하는 건 구글에서 검색하면 나옵니다. 그 중 많이 쓰는 코드를 올려놓겠습니다.

 

 

class AppSignatureHelper(context: Context) : ContextWrapper(context) {
    val appSignatures: ArrayList<String>
        @SuppressLint("PackageManagerGetSignatures") @RequiresApi(api = Build.VERSION_CODES.KITKAT) get() {
            val appCodes = ArrayList<String>()
            try {
                val packageName = packageName
                val packageManager = packageManager
                val signatures = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures
                signatures.mapNotNull { hash(packageName, it.toCharsString()) }.mapTo(appCodes) { String.format("%s", it) }
            } catch (e: PackageManager.NameNotFoundException) {
                Log.v(TAG, "Unable to find package to obtain hash.", e)
            }
            return appCodes
        }

    companion object {
        val TAG = AppSignatureHelper::class.java.simpleName!!
        private val HASH_TYPE = "SHA-256"
        private val NUM_HASHED_BYTES = 9
        private val NUM_BASE64_CHAR = 11

        @RequiresApi(api = Build.VERSION_CODES.KITKAT)
        private fun hash(packageName: String, signature: String): String? {
            val appInfo = packageName + " " + signature
            try {
                val messageDigest = MessageDigest.getInstance(HASH_TYPE)
                messageDigest.update(appInfo.toByteArray(StandardCharsets.UTF_8))
                var hashSignature = messageDigest.digest()

                // truncated into NUM_HASHED_BYTES
                hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES)
                // encode into Base64
                var base64Hash = Base64.getEncoder().encodeToString(hashSignature)
                base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR)

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

 

해당 코드는 쓰고 실제 product라면 지우시는 걸 추천합니다.

 

그리고 playstore에 올리신다면 playstore 용 hash를 따로 구하셔야합니다.

 

해당 블로그에 잘 설명되어 있습니다 !

https://jinchoi.oopy.io/android/etc/2

 

SMS Retriever API로 인증번호 자동 입력하기

유저에게 인증번호 자동 입력 기능을 제공하려면 구글의 SMS Retriever API를 활용해야 한다. 본 포스팅에서는 SMS Retriever API 연동 과정, 이를 위해 필요한 요구사항들, sms hash 계산 방법 및 유의사항

jinchoi.oopy.io

window에서 구할려고 하면 뭔가 복잡한거 같아서 microdoft store에서 터미널 받으시고 우분투를 받으신다음 우분투 터미널에서 구하시는게 훨씬 쉬웠습니다. 

 

class MainActivity : AppCompatActivity() {

    private var client: SmsRetrieverClient? = null
    private var task: Task<Void>? = null
    private lateinit var binding : ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        initClient()

        task!!.addOnSuccessListener { startSMSListener() }

        task!!.addOnFailureListener { e -> // Failed to start retriever, inspect Exception for more details

        }
    }

    private fun initClient() {
        client = SmsRetriever.getClient(this)
        task = client!!.startSmsRetriever()
    }

    private fun setAuthCode(authCode : String){
        binding.tvMsg.text = authCode
    }

    private fun startSMSListener() {
        MySmsReceiver.bindListener(object : SmsBroadcastReceiverListener {
            override fun onSuccess(authCode: String) {
                setAuthCode(authCode)
                initClient()
            }

            override fun onFailure() {
                initClient()
            }
        })
    }

    override fun onDestroy() {
        super.onDestroy()
        MySmsReceiver.unBindListener()
    }

}

이게 저만 그런건지는 모르겠는데 정상적으로 실행이 되면 receiver에서 onSuccess로 code가 옵니다.

 

그리고 나서 receiver를 다시 등록을 해주지 않으면 다음 문자부터는 또 읽어오지를 않습니다.

 

그래서 다시 initClient로 receiver를 붙여줬습니다.

 

그리고 destroy 될 때 receiver를 해제해주셔야 합니다.

 

 

틀린 부분이 있을 수도 있습니다 ! 참고해주세요 !

 

틀린 부분이 있으면 알려주시면 감사하겠습니다.

'android' 카테고리의 다른 글

Android Compose Navigation 기초 2 - Argument 보내기  (1) 2023.11.06
Android Compose Navigation 기초  (0) 2023.11.06