MOB-40 improved authentication UX

This commit is contained in:
Henrik Lepson 2021-11-25 18:09:45 +02:00
parent 762a8c8cc2
commit 0f6f31c995
9 changed files with 79 additions and 87 deletions

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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" />

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>