ระบบ biometric authentication (การยืนยันตัวตนด้วยข้อมูลทางชีวภาพ) บนมือถือนั้นมีใช้งานมาหลายปีแล้ว ซึ่งผู้ผลิตอุปกรณ์ก็นิยมนำเทคโนโลยีนี้ไปใช้กันอย่างแพร่หลาย เป็นไปได้ว่าผู้ใช้สมาร์ตโฟนทุกคนน่าจะเคยใช้งาน biometric authentication กันมาบ้างแล้ว ซึ่งเป็นระบบที่แอปพลิเคชันด้านการชำระเงินนิยมนำไปใช้งานเพื่อให้ผู้ใช้ได้รับความสะดวกสบายและมั่นใจในความปลอดภัย
ในฐานะที่ผมเป็นนักพัฒนาก็อยากจะอธิบายในบทความนี้ว่า เราสามารถนำ AndroidX Biometric API อันทรงพลังไปใช้งานกับแอปพลิเคชัน Android แบบง่ายๆ และใช้ประโยชน์มันให้เต็มประสิทธิภาพได้อย่างไร
FingerPrintManager เป็นคลาสที่เริ่มมีให้ใช้งานตั้งแต่ Android 6.0 (API level 23) ซึ่งเปิดให้เข้าถึงการทำงานของเครื่องสแกนลายนิ้วมือเท่านั้น อย่างไรก็ตาม มันก็ยังไม่มี UI ใดๆ ติดมาไว้เลย นักพัฒนาจึงต้อง implement UI ให้กับอุปกรณ์แต่ละเครื่องขึ้นมาเองเพื่อให้รองรับ biometric authentication ได้ ซึ่งการพัฒนา UI แบบปรับแต่งเองนั้นไม่ใช่เรื่องง่ายๆ เลย ยิ่งไปกว่านั้น เซนเซอร์แบบที่อยู่ในหน้าจอยังทำให้การพัฒนายากยิ่งขึ้นไปอีก
หลังจากที่ได้รับฟีดแบ็กจากนักพัฒนา ทำให้ Android 9 (API level 28) เริ่มนโยบายเกี่ยวกับการพัฒนา UI ของระบบตรวจสอบลายนิ้วมือที่มีมาตรฐานขึ้น นอกจากนั้น BiometricPrompt ยังเข้ามาเสริมในการทำงานของเซนเซอร์ที่รองรับได้มากกว่าแค่ลายนิ้วมือ ซึ่งไม่เพียงแต่ช่วยให้นักพัฒนามีชุดของ API ที่สามารถนำมาปรับปแต่งได้ง่ายๆ เพื่อสร้าง UI ที่ผู้ใช้คุ้นเคยและสอดคล้องกับระบบเท่านั้น แต่ยังช่วยลดความซับซ้อนในการสื่อสารกับฮาร์ดแวร์มากมายที่เกี่ยวข้องกับ biometric บนอุปกรณ์ต่างๆ อีกต่างหาก ทั้งยังทำให้บรรดา OEM ได้รับประโยชน์ไปด้วย เนื่องจากพวกเขาสามารถปรับแต่ง API เหล่านี้เพื่อ implement สไตล์ของตัวเองลงไปและสร้างระบบ biometric ใหม่ๆ ขึ้นมาได้โดยไม่ต้องกังวลว่านักพัฒนาจะปรับตัวตามได้หรือไม่
ใน Android 10 (API level 29) นั้น ทีมงานที่ดูแลด้าน Android Framework และความปลอดภัยได้ปรับปรุง AndroidX Biometric Library ให้ดียิ่งขึ้น ซึ่งทำให้การตรวจสอบ biometric ทั้งหมดใน Android 10 นั้นสามารถนำไปใช้งานกับอุปกรณ์ทุกชนิดที่รัน Android 6.0 (API level 23) ขึ้นไปได้ นอกจากการรองรับ biometric authentication ที่หลากหลายแล้ว ยังมีการเพิ่ม BiometricManager ขึ้นมาเพื่อช่วยให้นักพัฒนาสามารถเช็กได้ว่าอุปกรณ์นั้นๆ รองรับ biometric authentication หรือไม่ ด้วยการใช้ API เพียงตัวเดียว
ตามที่บอกไว้ก่อนหน้านี้ว่า AndroidX Biometric เริ่มมีให้ใช้ใน Android 10 เป็นต้นไป และรองรับการทำงานของ biometric authentication ตั้งแต่ API 23 เป็นต้นไป และการใช้งานรหัสปลดล็อกอื่นๆ อย่างเช่น รหัสผ่าน, PIN, และแพตเทิร์น นั้นก็รองรับย้อนไปถึง API 21 เลยทีเดียว
เอาละ มาถึงเนื้อหาส่วนที่น่าสนุกกันแล้ว
เราสามารถแบ่ง biometric authentication ออกเป็น 5 ขั้นตอนง่ายๆ ต่อไปนี้
เพิ่ม dependency ต่อไปนี้ลงไปในไฟล์ Gradle ที่ app level
implementation "androidx.biometric:biometric:1.1.0"
Biometric Library ทำให้ BiometricManager สามารถเช็กได้ว่าอุปกรณ์ของผู้ใช้รองรับฟีเจอร์ biometric authentication หรือไม่ ก่อนที่จะสร้างระบบยืนยันตัวตนขึ้นมา ถ้าหากว่าไม่ คุณก็สามารถแสดงข้อความบอกผู้ใช้ได้ว่าอุปกรณ์นั้นๆ ไม่รองรับ biometric authentication
biometricManager.canAuthenticate(int)
ค่า int นั้นแทนประเภทของระบบยืนยันตัวตนที่คุณต้องการ ซึ่งมีระบบยืนยันตัวตนที่คุณสามารถเลือกใช้งานได้อยู่ 3 ประเภท ได้แก่
- BIOMETRIC_WEAK สำหรับ non-crypto authentication
- BIOMETRIC_STRONG สำหรับ crypto-based authentication
- DEVICE_CREDENTIAL สำหรับ non-biometric Credential
private fun authenticateUser() {
val biometricManager = BiometricManager.from(this)
when (biometricManager.canAuthenticate(BIOMETRIC_WEAK)) {
BiometricManager.BIOMETRIC_SUCCESS ->
showBiometricPrompt()
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE ->
showMessage("There is no suitable hardware")
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE ->
showMessage("The hardware is unavailable. Try again later")
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED ->
showMessage("No biometric or device credential is enrolled")
BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED ->
showMessage("A security vulnerability has been discovered with one or more hardware sensors")
BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED ->
showMessage("The specified options are incompatible with the current Android version")
BiometricManager.BIOMETRIC_STATUS_UNKNOWN ->
showMessage("Unable to determine whether the user can authenticate")
}
}
เราสามารถรวม authenticator เหล่านี้ไว้ด้วยกันได้ เช่น BIOMETRIC_WEAK | DEVICE_CREDENTIAL
แต่ว่าไม่ใช่ทุก API ที่สามารถนำมารวมกันได้ ตัวอย่างเช่น DEVICE_CREDENTIAL นั้นไม่รองรับในเวอร์ชันก่อนหน้า API 30 ส่วน BIOMETRIC_STRONG | DEVICE_CREDENTIAL ก็ไม่รองรับใน API 28–29
PromptInfo object บรรจุ metadata และ configuration ต่างๆ เอาไว้สำหรับ BiometricPrompt ของเรา ซึ่งเราสามารถปรับแต่ง prompt ด้วย method เช่น setTitle, setSubtitle, setDescription และ setNegativeButtonText ได้ การใช้ setAllowedAuthenticatorsทำให้เราสามารถระบุได้ว่าเราอนุญาตให้ใช้รหัสปลดล็อก (PIN, แพตเทิร์น หรือ รหัสผ่าน) เป็น fallback หรือไม่ และใช้ setConfirmationRequired เพื่อเปิดหรือปิดการยืนยันแบบ explicit สำหรับ implicit authentication method เช่น ใบหน้า หรือ ม่านตา ได้ ซึ่งจะเป็นประโยชน์ในสถานการณ์ที่เราต้องการยืนยันการทำธุรกรรมจากผู้ใช้อีกครั้งก่อนที่จะเริ่มกระบวนการ
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("AndroidX Biometric")
.setSubtitle("Authenticate user via Biometric")
.setDescription("Please authenticate yourself here")
.setAllowedAuthenticators(BIOMETRIC_WEAK)
.setNegativeButtonText("Cancel")
.setConfirmationRequired(true)
.build()
อย่างไรก็ตาม การแสดงข้อความยืนยันแบบ explicit นั้นขึ้นอยู่กับอุปกรณ์แต่ละเครื่องด้วย ซึ่งทำให้ข้อความยืนยันแบบ explicit จะไม่ได้รับการแสดงในบางอุปกรณ์แม้ว่าจะตั้งค่าไว้เป็น true ผ่านทาง setConfirmationRequired method ก็ตาม
AndroidX BiometricPrompt ช่วยคุณทำในเรื่องยากๆ ไปหมดแล้วด้วยการรวบรวม UI metadata, แสดง authentication prompt ให้ผู้ใช้เห็น, และคืนค่าผลลัพธ์ของการยืนยันตัวตนในรูปแบบของ callback โดยมันจำเป็นต้องใช้ 4 component ต่อไปนี้จึงจะทำงานได้
private val authCallback = object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
showMessage("Authentication successful")
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
showMessage("Unrecoverable error => $errString")
}
override fun onAuthenticationFailed() {
showMessage("Could not recognise the user")
}
}
// Step 4
val biometricPrompt = BiometricPrompt(this@MainActivity, ContextCompat.getMainExecutor(this), authCallback)
ขั้นตอนสุดท้าย คือแสดง biometric prompt ให้ผู้ใช้เห็นและบอกให้ยืนยันตัวตนbiometricPrompt.authenticate(promptInfo)
โดย prompt นี้สามารถยกเลิกได้ด้วยคำสั่งbiometricPrompt.cancelAuthentication()
แต่ถ้าหากแอปฯ ของคุณต้องการให้ผู้ใช้ยืนยันตัวตนด้วยStrong biometric หรือใช้งานการเข้ารหัสแบบ cryptographic ใน KeyStore คุณควรใช้ authenticate(PromptInfo, CryptoObject)
แทน
พลังของ AndroidX Biometric Library อยู่ที่การลดความยุ่งยากซับซ้อนในการสื่อสารระหว่างเซนเซอร์ biometric authentication หลากหลายประเภท (เช่น ลายนิ้วมือ, ใบหน้า, และม่านตา) ลงได้ และมี API ที่เรียบง่ายให้นักพัฒนาและ OEM สามารถปรับแต่งได้ตามต้องการ อย่างไรก็ตาม ในตอนนี้ยังไม่มีวิธีตรวจสอบว่าอุปกรณ์นั้นๆ มีเซนเซอร์ biometric แบบไหนด้วยการใช้ไลบรารีนี้ ดังนั้น คุณอาจต้องแสดงข้อความให้ผู้ใช้ทราบว่าใช้งานข้อมูล biometric แทนที่จะแสดงข้อความที่เฉพาะเจาะจงอย่างเช่น ลายนิ้วมือ หรือ ใบหน้า เป็นต้น
ผมหวังว่าจะช่วยให้คุณรู้อะไรใหม่ๆ จากบทความนี้ ขอให้สนุกกับการเรียนรู้!
Sifat UI Haque, Android Tech Lead ที่ Seven Peaks Software