Skip to content

Commit 7738fcb

Browse files
committed
Use email or alias
Add request for username
1 parent e09a4f3 commit 7738fcb

8 files changed

Lines changed: 220 additions & 5 deletions

File tree

app/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ android {
2828
sourceCompatibility JavaVersion.VERSION_1_8
2929
targetCompatibility JavaVersion.VERSION_1_8
3030
}
31+
kotlinOptions {
32+
jvmTarget = '1.8'
33+
}
3134
buildFeatures {
3235
viewBinding true
3336
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package com.azuresamples.msalnativeauthandroidkotlinsampleapp
2+
3+
import android.app.AlertDialog
4+
import android.os.Bundle
5+
import android.view.LayoutInflater
6+
import android.view.View
7+
import android.view.ViewGroup
8+
import android.widget.Toast
9+
import androidx.fragment.app.Fragment
10+
import com.azuresamples.msalnativeauthandroidkotlinsampleapp.databinding.FragmentSignUpAttributesBinding
11+
import com.microsoft.identity.nativeauth.UserAttributes
12+
import com.microsoft.identity.nativeauth.parameters.NativeAuthSignInContinuationParameters
13+
import com.microsoft.identity.nativeauth.statemachine.errors.SignInContinuationError
14+
import com.microsoft.identity.nativeauth.statemachine.errors.SignUpSubmitAttributesError
15+
import com.microsoft.identity.nativeauth.statemachine.results.SignInResult
16+
import com.microsoft.identity.nativeauth.statemachine.results.SignUpResult
17+
import com.microsoft.identity.nativeauth.statemachine.states.SignInContinuationState
18+
import com.microsoft.identity.nativeauth.statemachine.states.SignUpAttributesRequiredState
19+
import kotlinx.coroutines.CoroutineScope
20+
import kotlinx.coroutines.Dispatchers
21+
import kotlinx.coroutines.launch
22+
23+
class SignUpAttributesFragment : Fragment() {
24+
private lateinit var currentState: SignUpAttributesRequiredState
25+
private var _binding: FragmentSignUpAttributesBinding? = null
26+
private val binding get() = _binding!!
27+
28+
companion object {
29+
private val TAG = SignUpAttributesFragment::class.java.simpleName
30+
}
31+
32+
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
33+
_binding = FragmentSignUpAttributesBinding.inflate(inflater, container, false)
34+
35+
val bundle = this.arguments
36+
currentState = (bundle?.getParcelable(Constants.STATE) as? SignUpAttributesRequiredState)!!
37+
38+
init()
39+
40+
return binding.root
41+
}
42+
43+
private fun init() {
44+
initializeButtonListeners()
45+
}
46+
47+
private fun initializeButtonListeners() {
48+
binding.submitAttributes.setOnClickListener {
49+
submitAttributes()
50+
}
51+
52+
binding.cancelAttributes.setOnClickListener {
53+
finish()
54+
}
55+
}
56+
57+
private fun submitAttributes() {
58+
CoroutineScope(Dispatchers.Main).launch {
59+
val username = binding.usernameText.text.toString()
60+
61+
val attributes = UserAttributes.Builder()
62+
.customAttribute("username", username)
63+
.build()
64+
65+
val actionResult = currentState.submitAttributes(attributes)
66+
67+
when (actionResult) {
68+
is SignUpResult.Complete -> {
69+
Toast.makeText(requireContext(), getString(R.string.sign_up_successful_message), Toast.LENGTH_SHORT).show()
70+
signInAfterSignUp(
71+
nextState = actionResult.nextState
72+
)
73+
}
74+
is SignUpResult.AttributesRequired -> {
75+
displayDialog(getString(R.string.unexpected_sdk_result_title), actionResult.toString())
76+
}
77+
is SignUpSubmitAttributesError -> {
78+
displayDialog(getString(R.string.unexpected_sdk_error_title), actionResult.exception?.message ?: actionResult.errorMessage)
79+
}
80+
}
81+
}
82+
}
83+
84+
private suspend fun signInAfterSignUp(nextState: SignInContinuationState) {
85+
val parameters = NativeAuthSignInContinuationParameters()
86+
val actionResult = nextState.signIn(parameters)
87+
88+
when (actionResult) {
89+
is SignInResult.Complete -> {
90+
Toast.makeText(
91+
requireContext(),
92+
getString(R.string.sign_in_successful_message),
93+
Toast.LENGTH_SHORT
94+
).show()
95+
finish()
96+
}
97+
is SignInContinuationError -> {
98+
displayDialog(getString(R.string.msal_exception_title), actionResult.exception?.message ?: actionResult.errorMessage)
99+
}
100+
is SignInResult.CodeRequired,
101+
is SignInResult.PasswordRequired -> {
102+
displayDialog(getString(R.string.unexpected_sdk_result_title), actionResult.toString())
103+
}
104+
}
105+
}
106+
107+
private fun displayDialog(error: String?, message: String?) {
108+
val builder = AlertDialog.Builder(requireContext())
109+
builder.setTitle(error)
110+
.setMessage(message)
111+
val alertDialog = builder.create()
112+
alertDialog.show()
113+
}
114+
115+
private fun finish() {
116+
requireActivity().supportFragmentManager.popBackStackImmediate()
117+
}
118+
}

app/src/main/java/com/azuresamples/msalnativeauthandroidkotlinsampleapp/SignUpCodeFragment.kt

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import com.microsoft.identity.nativeauth.statemachine.results.SignInResult
1616
import com.microsoft.identity.nativeauth.statemachine.results.SignUpResendCodeResult
1717
import com.microsoft.identity.nativeauth.statemachine.results.SignUpResult
1818
import com.microsoft.identity.nativeauth.statemachine.states.SignInContinuationState
19+
import com.microsoft.identity.nativeauth.statemachine.states.SignUpAttributesRequiredState
1920
import com.microsoft.identity.nativeauth.statemachine.states.SignUpCodeRequiredState
2021
import kotlinx.coroutines.CoroutineScope
2122
import kotlinx.coroutines.Dispatchers
@@ -68,7 +69,11 @@ class SignUpCodeFragment : Fragment() {
6869
nextState = actionResult.nextState
6970
)
7071
}
71-
is SignUpResult.AttributesRequired,
72+
is SignUpResult.AttributesRequired -> {
73+
navigateToAttributes(
74+
nextState = actionResult.nextState
75+
)
76+
}
7277
is SignUpResult.PasswordRequired -> {
7378
displayDialog(getString(R.string.unexpected_sdk_result_title), actionResult.toString())
7479
}
@@ -144,6 +149,23 @@ class SignUpCodeFragment : Fragment() {
144149
alertDialog.show()
145150
}
146151

152+
private fun navigateToAttributes(nextState: SignUpAttributesRequiredState) {
153+
val bundle = Bundle()
154+
bundle.putParcelable(Constants.STATE, nextState)
155+
val fragment = SignUpAttributesFragment()
156+
fragment.arguments = bundle
157+
158+
val fragmentManager = requireActivity().supportFragmentManager
159+
// Pop the code fragment first, then show the attributes fragment
160+
fragmentManager.popBackStackImmediate()
161+
fragmentManager
162+
.beginTransaction()
163+
.setReorderingAllowed(true)
164+
.addToBackStack(fragment::class.java.name)
165+
.replace(R.id.scenario_fragment, fragment)
166+
.commit()
167+
}
168+
147169
private fun finish() {
148170
requireActivity().supportFragmentManager.popBackStackImmediate()
149171
}

app/src/main/res/layout/fragment_email_password.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
android:layout_width="wrap_content"
2828
android:layout_height="wrap_content"
2929
android:layout_marginTop="@dimen/dimens_15dp"
30-
android:text="@string/attribute_email"
30+
android:text="@string/attribute_email_or_username"
3131
app:layout_constraintStart_toStartOf="parent"
3232
app:layout_constraintTop_toBottomOf="@+id/hint_text"
3333
style="@style/TextViewStyle" />

app/src/main/res/layout/fragment_email_sspr.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
android:layout_width="wrap_content"
2727
android:layout_height="wrap_content"
2828
android:layout_marginTop="@dimen/dimens_15dp"
29-
android:text="@string/attribute_email"
29+
android:text="@string/attribute_email_or_username"
3030
app:layout_constraintStart_toStartOf="parent"
3131
app:layout_constraintTop_toBottomOf="@+id/hint_text"
3232
style="@style/TextViewStyle" />
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<androidx.constraintlayout.widget.ConstraintLayout
3+
xmlns:android="http://schemas.android.com/apk/res/android"
4+
xmlns:app="http://schemas.android.com/apk/res-auto"
5+
xmlns:tools="http://schemas.android.com/tools"
6+
android:layout_width="match_parent"
7+
android:layout_height="wrap_content">
8+
9+
<TextView
10+
android:id="@+id/hint_text"
11+
android:layout_width="match_parent"
12+
android:layout_height="wrap_content"
13+
android:layout_margin="@dimen/dimens_15dp"
14+
android:layout_marginStart="@dimen/dimens_15dp"
15+
android:layout_marginLeft="@dimen/dimens_15dp"
16+
android:gravity="start|center_horizontal"
17+
android:text="@string/attributes_hint_text_value"
18+
app:layout_constraintStart_toStartOf="parent"
19+
app:layout_constraintTop_toTopOf="parent" />
20+
21+
<TextView
22+
android:id="@+id/username_hint"
23+
android:layout_width="wrap_content"
24+
android:layout_height="wrap_content"
25+
android:layout_marginTop="@dimen/dimens_15dp"
26+
android:text="@string/attribute_username"
27+
app:layout_constraintTop_toBottomOf="@id/hint_text"
28+
app:layout_constraintStart_toStartOf="parent"
29+
style="@style/TextViewStyle" />
30+
31+
<com.google.android.material.textfield.TextInputLayout
32+
android:id="@+id/username_layout"
33+
android:layout_width="match_parent"
34+
android:layout_height="wrap_content"
35+
app:layout_constraintEnd_toEndOf="parent"
36+
app:layout_constraintStart_toStartOf="parent"
37+
app:layout_constraintTop_toBottomOf="@+id/username_hint"
38+
style="@style/TextInputLayoutStyle">
39+
40+
<com.google.android.material.textfield.TextInputEditText
41+
android:id="@+id/username_text"
42+
android:layout_width="match_parent"
43+
android:inputType="text"
44+
tools:ignore="SpeakableTextPresentCheck"
45+
style="@style/TextInputEditTextStyle" />
46+
47+
</com.google.android.material.textfield.TextInputLayout>
48+
49+
<Button
50+
android:id="@+id/submit_attributes"
51+
android:text="@string/submit_attributes"
52+
android:layout_marginTop="@dimen/dimens_30dp"
53+
app:layout_constraintEnd_toEndOf="parent"
54+
app:layout_constraintStart_toStartOf="parent"
55+
app:layout_constraintTop_toBottomOf="@+id/username_layout"
56+
style="@style/ActionButtonStyle" />
57+
58+
<Button
59+
android:id="@+id/cancel_attributes"
60+
android:text="@string/cancel_message"
61+
android:layout_marginTop="@dimen/dimens_15dp"
62+
app:layout_constraintBottom_toBottomOf="parent"
63+
app:layout_constraintEnd_toEndOf="parent"
64+
app:layout_constraintStart_toStartOf="parent"
65+
app:layout_constraintTop_toBottomOf="@+id/submit_attributes"
66+
style="@style/ActionButtonStyle" />
67+
68+
</androidx.constraintlayout.widget.ConstraintLayout>

app/src/main/res/values/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
<string name="hint_text_value">"Please fill in the required information:"</string>
3232
<string name="hint_password_text_value">"Please submit a new password."</string>
3333
<string name="oob_hint_text_value">"Please enter the code that you received in your email."</string>
34+
<string name="attributes_hint_text_value">"Additional attributes are required, please enter them below."</string>
3435
<string name="mfa_challenge_hint_text_value">"Please enter the challenge that you received in your challengeChannel loginHint."</string>
3536
<string name="strong_auth_challenge_hint_text_value">"Please enter the verification code that was send to your %1$s %2$s. This step is needed to register a new strong authentication method."</string>
3637
<string name="strong_auth_email_hint_text_value">"For Multi-factor authentication, you can enter a separate email address (optional). If you don't provide one, we will use your primary email address."</string>
@@ -64,13 +65,16 @@
6465

6566
<!-- Fields -->
6667
<string name="attribute_email">"Email"<font color='@android:color/red'>*</font></string>
68+
<string name="attribute_email_or_username">"Email or Username"<font color='@android:color/red'>*</font></string>
6769
<string name="attribute_phone_number">"Phone number"<font color='@android:color/red'>*</font></string>
6870
<string name="attribute_password">"Password"<font color='@android:color/red'>*</font></string>
6971
<string name="attribute_country">"Country"</string>
7072
<string name="attribute_city">"City"</string>
7173
<string name="attribute_given">"Given name"</string>
7274
<string name="attribute_surname">"Surname"</string>
7375
<string name="attribute_otp">"Code"</string>
76+
<string name="attribute_username">"Username"</string>
77+
<string name="submit_attributes">"Submit"</string>
7478
<string name="attribute_mfa_challenge">"Challenge"</string>
7579

7680
<!-- Results -->

gradle/versions.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ ext {
77
compileSdkVersion = 34
88
buildToolsVersion = "28.0.3"
99
coreKtxVersion = "1.6.0"
10-
kotlinXCoroutinesVersion = "1.6.4"
11-
kotlinVersion = '1.7.21'
10+
kotlinXCoroutinesVersion = "1.7.3"
11+
kotlinVersion = '1.9.21'
1212

1313
// Plugins
1414
gradleVersion = '8.11.1'

0 commit comments

Comments
 (0)