mirror of
https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC.git
synced 2024-12-22 12:30:16 +02:00
MOB-40 improved authentication UX
This commit is contained in:
parent
762a8c8cc2
commit
0f6f31c995
@ -4,6 +4,7 @@ import android.app.Activity
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.nfc.NfcAdapter
|
import android.nfc.NfcAdapter
|
||||||
|
import android.nfc.TagLostException
|
||||||
import android.nfc.tech.IsoDep
|
import android.nfc.tech.IsoDep
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.CountDownTimer
|
import android.os.CountDownTimer
|
||||||
@ -69,12 +70,33 @@ class AuthFragment : Fragment() {
|
|||||||
goToTheStart()
|
goToTheStart()
|
||||||
}
|
}
|
||||||
}.start()
|
}.start()
|
||||||
//binding!!.nextButton.visibility = View.INVISIBLE
|
binding!!.nextButton.visibility = View.GONE
|
||||||
binding!!.nextButton.setOnClickListener { goToNextFragment() }
|
binding!!.nextButton.setOnClickListener { goToNextFragment() }
|
||||||
binding!!.cancelButton.setOnClickListener { goToTheStart() }
|
binding!!.cancelButton.setOnClickListener { goToTheStart() }
|
||||||
val adapter = NfcAdapter.getDefaultAdapter(activity)
|
val adapter = NfcAdapter.getDefaultAdapter(activity)
|
||||||
if (adapter != null)
|
if (adapter != null)
|
||||||
getInfoFromIdCard(adapter)
|
getInfoFromIdCard(adapter)
|
||||||
|
else { // If we don't have access to an NFC adapter then we can't detect an ID card, maybe should tell the user reason as well
|
||||||
|
goToTheStart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun goToNextFragment() {
|
||||||
|
timer.cancel()
|
||||||
|
val action = AuthFragmentDirections.actionAuthFragmentToResultFragment(mobile = args.mobile)
|
||||||
|
findNavController().navigate(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun goToTheStart() {
|
||||||
|
viewModel.clearUserInfo()
|
||||||
|
timer.cancel()
|
||||||
|
if (args.mobile) {
|
||||||
|
val resultIntent = Intent()
|
||||||
|
requireActivity().setResult(AppCompatActivity.RESULT_CANCELED, resultIntent)
|
||||||
|
requireActivity().finish()
|
||||||
|
} else {
|
||||||
|
requireActivity().finishAndRemoveTask()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getInfoFromIdCard(adapter: NfcAdapter) {
|
private fun getInfoFromIdCard(adapter: NfcAdapter) {
|
||||||
@ -88,33 +110,34 @@ class AuthFragment : Fragment() {
|
|||||||
card.use {
|
card.use {
|
||||||
try {
|
try {
|
||||||
val comms = Comms(it, viewModel.userCan)
|
val comms = Comms(it, viewModel.userCan)
|
||||||
if (args.auth) {
|
|
||||||
val jws = Authenticator(comms).authenticate(
|
val jws = Authenticator(comms).authenticate(
|
||||||
intentParameters.challenge,
|
intentParameters.challenge,
|
||||||
intentParameters.origin,
|
intentParameters.origin,
|
||||||
viewModel.userPin
|
viewModel.userPin
|
||||||
)
|
)
|
||||||
intentParameters.setToken(jws)
|
intentParameters.setToken(jws)
|
||||||
} else {
|
|
||||||
val response = comms.readPersonalData(byteArrayOf(1, 2, 6, 3, 4, 8))
|
|
||||||
viewModel.setUserFirstName(response[1])
|
|
||||||
viewModel.setUserLastName(response[0])
|
|
||||||
viewModel.setUserIdentificationNumber(response[2])
|
|
||||||
viewModel.setGender(response[3])
|
|
||||||
viewModel.setCitizenship(response[4])
|
|
||||||
viewModel.setExpiration(response[5])
|
|
||||||
}
|
|
||||||
requireActivity().runOnUiThread {
|
requireActivity().runOnUiThread {
|
||||||
binding!!.timeCounter.text = getString(R.string.data_read)
|
goToNextFragment()
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
requireActivity().runOnUiThread {
|
when(e) {
|
||||||
binding!!.timeCounter.text = getString(R.string.no_success)
|
is TagLostException -> requireActivity().runOnUiThread { binding!!.timeCounter.text = getString(R.string.id_card_removed_early) }
|
||||||
|
else -> {
|
||||||
|
when ("invalid pin") {
|
||||||
|
in e.message.toString().lowercase() -> requireActivity().runOnUiThread {
|
||||||
|
val messagePieces = e.message.toString().split(" ")
|
||||||
|
binding!!.timeCounter.text = getString(R.string.wrong_pin, messagePieces[messagePieces.size - 1])
|
||||||
|
viewModel.deletePin(requireContext())
|
||||||
}
|
}
|
||||||
// If the CAN is wrong we will also delete the saved CAN so that the user won't use it again.
|
else -> {
|
||||||
|
binding!!.timeCounter.text = getString(R.string.no_success)
|
||||||
viewModel.deleteCan(requireContext())
|
viewModel.deleteCan(requireContext())
|
||||||
// Gives user some time to read the error message
|
}
|
||||||
Thread.sleep(1000)
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Give user some time to read the error message
|
||||||
|
Thread.sleep(2000)
|
||||||
goToTheStart()
|
goToTheStart()
|
||||||
} finally {
|
} finally {
|
||||||
adapter.disableReaderMode(activity)
|
adapter.disableReaderMode(activity)
|
||||||
@ -123,33 +146,6 @@ class AuthFragment : Fragment() {
|
|||||||
}, NfcAdapter.FLAG_READER_NFC_A, null)
|
}, NfcAdapter.FLAG_READER_NFC_A, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun goToNextFragment() {
|
|
||||||
timer.cancel()
|
|
||||||
if (args.auth) {
|
|
||||||
val action = AuthFragmentDirections.actionAuthFragmentToResultFragment(mobile = args.mobile)
|
|
||||||
findNavController().navigate(action)
|
|
||||||
} else {
|
|
||||||
findNavController().navigate(R.id.action_authFragment_to_userFragment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun goToTheStart() {
|
|
||||||
viewModel.clearUserInfo()
|
|
||||||
timer.cancel()
|
|
||||||
if (args.reading) {
|
|
||||||
findNavController().navigate(R.id.action_authFragment_to_homeFragment)
|
|
||||||
} else {
|
|
||||||
if (!args.mobile) {
|
|
||||||
//Currently for some reason the activity is not killed entirely. Must be looked into further.
|
|
||||||
requireActivity().finishAndRemoveTask()
|
|
||||||
} else {
|
|
||||||
val resultIntent = Intent()
|
|
||||||
requireActivity().setResult(AppCompatActivity.RESULT_CANCELED, resultIntent)
|
|
||||||
requireActivity().finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
binding = null
|
binding = null
|
||||||
|
@ -78,6 +78,8 @@ class HomeFragment : Fragment() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Method that starts the authentication use case.
|
* Method that starts the authentication use case.
|
||||||
|
*
|
||||||
|
* NOTE: Comment out try-catch block when testing without backend
|
||||||
*/
|
*/
|
||||||
private fun startAuthentication(mobile: Boolean) {
|
private fun startAuthentication(mobile: Boolean) {
|
||||||
try {
|
try {
|
||||||
|
@ -39,10 +39,21 @@ class ResultFragment : Fragment() {
|
|||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
binding!!.resultBackButton.visibility = View.GONE
|
|
||||||
postToken()
|
postToken()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only used when the MobileAuthApp was launched by an app. Not for website use.
|
||||||
|
*/
|
||||||
|
private fun createResponse(success: Boolean = true, result: String = "noResult", token: String = "noToken") {
|
||||||
|
val responseCode = if (success) AppCompatActivity.RESULT_OK else AppCompatActivity.RESULT_CANCELED
|
||||||
|
val resultIntent = Intent()
|
||||||
|
resultIntent.putExtra("result", result)
|
||||||
|
resultIntent.putExtra("token", token)
|
||||||
|
requireActivity().setResult(responseCode, resultIntent)
|
||||||
|
requireActivity().finish()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes a POST request to the backend server with a tokenItem
|
* Makes a POST request to the backend server with a tokenItem
|
||||||
*/
|
*/
|
||||||
@ -77,18 +88,6 @@ class ResultFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Only used when the MobileAuthApp was launched by an app. Not for website use.
|
|
||||||
*/
|
|
||||||
private fun createResponse(success: Boolean = true, result: String = "noResult", token: String = "noToken") {
|
|
||||||
val responseCode = if (success) AppCompatActivity.RESULT_OK else AppCompatActivity.RESULT_CANCELED
|
|
||||||
val resultIntent = Intent()
|
|
||||||
resultIntent.putExtra("result", result)
|
|
||||||
resultIntent.putExtra("token", token)
|
|
||||||
requireActivity().setResult(responseCode, resultIntent)
|
|
||||||
requireActivity().finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
binding = null
|
binding = null
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="@dimen/padding">
|
android:padding="@dimen/padding_small">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/auth_fragment_instruction"
|
android:id="@+id/auth_fragment_instruction"
|
||||||
@ -36,10 +36,11 @@
|
|||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/nfc_logo"
|
android:id="@+id/nfc_logo"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="@dimen/logo_big"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="@dimen/logo_big"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:layout_margin="@dimen/margin"
|
android:layout_margin="@dimen/margin"
|
||||||
|
android:padding="@dimen/margin_huge"
|
||||||
android:src="@drawable/nfc_logo" />
|
android:src="@drawable/nfc_logo" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -47,7 +48,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="@dimen/margin"
|
android:layout_margin="@dimen/margin"
|
||||||
android:textSize="@dimen/helper_text"
|
android:textSize="@dimen/regular_text"
|
||||||
app:layout_constraintTop_toBottomOf="@id/auth_fragment_instruction"
|
app:layout_constraintTop_toBottomOf="@id/auth_fragment_instruction"
|
||||||
tools:text="@string/time_left" />
|
tools:text="@string/time_left" />
|
||||||
|
|
||||||
@ -72,9 +73,9 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/margin_big"
|
android:layout_marginTop="@dimen/margin_big"
|
||||||
|
android:layout_marginStart="@dimen/padding_tiny"
|
||||||
android:text="@string/cancel_text"
|
android:text="@string/cancel_text"
|
||||||
android:textSize="@dimen/regular_text"
|
android:textSize="@dimen/regular_text"
|
||||||
app:layout_constraintEnd_toStartOf="@id/next_button"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/card_view" />
|
app:layout_constraintTop_toBottomOf="@id/card_view" />
|
||||||
|
|
||||||
|
@ -4,14 +4,14 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:padding="24dp"
|
android:padding="@dimen/padding"
|
||||||
tools:context=".ResultFragment">
|
tools:context=".ResultFragment">
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:id="@+id/can_status"
|
android:id="@+id/can_status"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="12dp"
|
android:layout_margin="@dimen/margin"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
@ -27,33 +27,23 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/result_text"
|
android:id="@+id/result_text"
|
||||||
android:text="@string/result_text"
|
android:text="@string/result_text"
|
||||||
android:textSize="20sp"
|
android:textSize="@dimen/regular_text"
|
||||||
android:padding="12dp"
|
android:padding="@dimen/padding_small"
|
||||||
android:layout_marginVertical="6dp"
|
android:layout_marginVertical="@dimen/margin_small"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/result_info_text"
|
android:id="@+id/result_info_text"
|
||||||
android:text="@string/result_info"
|
android:text="@string/result_info"
|
||||||
android:padding="12dp"
|
android:padding="@dimen/padding_small"
|
||||||
android:textSize="16sp"
|
android:textSize="@dimen/regular_text"
|
||||||
android:layout_marginVertical="6dp"
|
android:layout_marginVertical="@dimen/margin_small"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/result_back_button"
|
|
||||||
android:text="@string/return_text"
|
|
||||||
android:layout_marginHorizontal="12dp"
|
|
||||||
android:layout_marginVertical="6dp"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -99,4 +99,5 @@
|
|||||||
<string name="nfc_reading_error">The provided CAN does not match the ID card</string>
|
<string name="nfc_reading_error">The provided CAN does not match the ID card</string>
|
||||||
<string name="id_card_removed_early">ID card was removed too early</string>
|
<string name="id_card_removed_early">ID card was removed too early</string>
|
||||||
<string name="try_again">Try Again</string>
|
<string name="try_again">Try Again</string>
|
||||||
|
<string name="wrong_pin">Wrong PIN 1. Tries on the card left %s</string>
|
||||||
</resources>
|
</resources>
|
@ -97,4 +97,5 @@
|
|||||||
<string name="nfc_reading_error">The provided CAN does not match the ID card</string>
|
<string name="nfc_reading_error">The provided CAN does not match the ID card</string>
|
||||||
<string name="id_card_removed_early">ID card was removed too early</string>
|
<string name="id_card_removed_early">ID card was removed too early</string>
|
||||||
<string name="try_again">Try Again</string>
|
<string name="try_again">Try Again</string>
|
||||||
|
<string name="wrong_pin">Wrong PIN 1. Tries on the card left %s</string>
|
||||||
</resources>
|
</resources>
|
@ -10,4 +10,5 @@
|
|||||||
<dimen name="regular_text">24sp</dimen>
|
<dimen name="regular_text">24sp</dimen>
|
||||||
<dimen name="headline_text">32sp</dimen>
|
<dimen name="headline_text">32sp</dimen>
|
||||||
<dimen name="helper_text">16sp</dimen>
|
<dimen name="helper_text">16sp</dimen>
|
||||||
|
<dimen name="logo_big">128dp</dimen>
|
||||||
</resources>
|
</resources>
|
@ -97,4 +97,5 @@
|
|||||||
<string name="nfc_reading_error">The provided CAN does not match the ID card</string>
|
<string name="nfc_reading_error">The provided CAN does not match the ID card</string>
|
||||||
<string name="id_card_removed_early">ID card was removed too early</string>
|
<string name="id_card_removed_early">ID card was removed too early</string>
|
||||||
<string name="try_again">Try Again</string>
|
<string name="try_again">Try Again</string>
|
||||||
|
<string name="wrong_pin">Wrong PIN 1. Tries on the card left %s</string>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in New Issue
Block a user