Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
viewBinding true
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package com.azuresamples.msalnativeauthandroidkotlinsampleapp

import android.app.AlertDialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import com.azuresamples.msalnativeauthandroidkotlinsampleapp.databinding.FragmentSignUpAttributesBinding
import com.microsoft.identity.nativeauth.UserAttributes
import com.microsoft.identity.nativeauth.parameters.NativeAuthSignInContinuationParameters
import com.microsoft.identity.nativeauth.statemachine.errors.SignInContinuationError
import com.microsoft.identity.nativeauth.statemachine.errors.SignUpSubmitAttributesError
import com.microsoft.identity.nativeauth.statemachine.results.SignInResult
import com.microsoft.identity.nativeauth.statemachine.results.SignUpResult
import com.microsoft.identity.nativeauth.statemachine.states.SignInContinuationState
import com.microsoft.identity.nativeauth.statemachine.states.SignUpAttributesRequiredState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class SignUpAttributesFragment : Fragment() {
private lateinit var currentState: SignUpAttributesRequiredState
private var _binding: FragmentSignUpAttributesBinding? = null
private val binding get() = _binding!!

companion object {
private val TAG = SignUpAttributesFragment::class.java.simpleName
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FragmentSignUpAttributesBinding.inflate(inflater, container, false)

val bundle = this.arguments
currentState = (bundle?.getParcelable(Constants.STATE) as? SignUpAttributesRequiredState)!!

init()

return binding.root
}

private fun init() {
initializeButtonListeners()
}

private fun initializeButtonListeners() {
binding.submitAttributes.setOnClickListener {
submitAttributes()
}

binding.cancelAttributes.setOnClickListener {
finish()
}
}

private fun submitAttributes() {
CoroutineScope(Dispatchers.Main).launch {
val username = binding.usernameText.text.toString()

val attributes = UserAttributes.Builder()
.flatUsername(username)
.build()

val actionResult = currentState.submitAttributes(attributes)

when (actionResult) {
is SignUpResult.Complete -> {
Toast.makeText(requireContext(), getString(R.string.sign_up_successful_message), Toast.LENGTH_SHORT).show()
signInAfterSignUp(
nextState = actionResult.nextState
)
}
is SignUpResult.AttributesRequired -> {
displayDialog(getString(R.string.unexpected_sdk_result_title), actionResult.toString())
}
is SignUpSubmitAttributesError -> {
displayDialog(getString(R.string.unexpected_sdk_error_title), actionResult.exception?.message ?: actionResult.errorMessage)
}
}
}
}

private suspend fun signInAfterSignUp(nextState: SignInContinuationState) {
val parameters = NativeAuthSignInContinuationParameters()
val actionResult = nextState.signIn(parameters)

when (actionResult) {
is SignInResult.Complete -> {
Toast.makeText(
requireContext(),
getString(R.string.sign_in_successful_message),
Toast.LENGTH_SHORT
).show()
finish()
}
is SignInContinuationError -> {
displayDialog(getString(R.string.msal_exception_title), actionResult.exception?.message ?: actionResult.errorMessage)
}
is SignInResult.CodeRequired,
is SignInResult.PasswordRequired -> {
displayDialog(getString(R.string.unexpected_sdk_result_title), actionResult.toString())
}
}
}

private fun displayDialog(error: String?, message: String?) {
val builder = AlertDialog.Builder(requireContext())
builder.setTitle(error)
.setMessage(message)
val alertDialog = builder.create()
alertDialog.show()
}

private fun finish() {
requireActivity().supportFragmentManager.popBackStackImmediate()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import com.microsoft.identity.nativeauth.statemachine.results.SignInResult
import com.microsoft.identity.nativeauth.statemachine.results.SignUpResendCodeResult
import com.microsoft.identity.nativeauth.statemachine.results.SignUpResult
import com.microsoft.identity.nativeauth.statemachine.states.SignInContinuationState
import com.microsoft.identity.nativeauth.statemachine.states.SignUpAttributesRequiredState
import com.microsoft.identity.nativeauth.statemachine.states.SignUpCodeRequiredState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -68,7 +69,18 @@ class SignUpCodeFragment : Fragment() {
nextState = actionResult.nextState
)
}
is SignUpResult.AttributesRequired,
is SignUpResult.AttributesRequired -> {
// Custom implementation for Flat Username / Alias
if (actionResult.requiredAttributes.size == 1 &&
actionResult.requiredAttributes.any { it.name == "flatusername" }
) {
navigateToAttributes(
nextState = actionResult.nextState
)
} else {
displayDialog(getString(R.string.unexpected_sdk_result_title), actionResult.toString())
}
}
is SignUpResult.PasswordRequired -> {
displayDialog(getString(R.string.unexpected_sdk_result_title), actionResult.toString())
}
Expand Down Expand Up @@ -144,6 +156,23 @@ class SignUpCodeFragment : Fragment() {
alertDialog.show()
}

private fun navigateToAttributes(nextState: SignUpAttributesRequiredState) {
val bundle = Bundle()
bundle.putParcelable(Constants.STATE, nextState)
val fragment = SignUpAttributesFragment()
fragment.arguments = bundle

val fragmentManager = requireActivity().supportFragmentManager
// Pop the code fragment first, then show the attributes fragment
fragmentManager.popBackStackImmediate()
fragmentManager
.beginTransaction()
.setReorderingAllowed(true)
.addToBackStack(fragment::class.java.name)
.replace(R.id.scenario_fragment, fragment)
.commit()
}

private fun finish() {
requireActivity().supportFragmentManager.popBackStackImmediate()
}
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/layout/fragment_email_password.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimens_15dp"
android:text="@string/attribute_email"
android:text="@string/attribute_email_or_username"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/hint_text"
style="@style/TextViewStyle" />
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/layout/fragment_email_sspr.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimens_15dp"
android:text="@string/attribute_email"
android:text="@string/attribute_email_or_username"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/hint_text"
style="@style/TextViewStyle" />
Expand Down
68 changes: 68 additions & 0 deletions app/src/main/res/layout/fragment_sign_up_attributes.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<TextView
android:id="@+id/hint_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/dimens_15dp"
android:layout_marginStart="@dimen/dimens_15dp"
android:layout_marginLeft="@dimen/dimens_15dp"
android:gravity="start|center_horizontal"
android:text="@string/attributes_hint_text_value"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/username_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimens_15dp"
android:text="@string/attribute_username"
app:layout_constraintTop_toBottomOf="@id/hint_text"
app:layout_constraintStart_toStartOf="parent"
style="@style/TextViewStyle" />

<com.google.android.material.textfield.TextInputLayout
android:id="@+id/username_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/username_hint"
style="@style/TextInputLayoutStyle">

<com.google.android.material.textfield.TextInputEditText
android:id="@+id/username_text"
android:layout_width="match_parent"
android:inputType="text"
tools:ignore="SpeakableTextPresentCheck"
style="@style/TextInputEditTextStyle" />

</com.google.android.material.textfield.TextInputLayout>

<Button
android:id="@+id/submit_attributes"
android:text="@string/submit_attributes"
android:layout_marginTop="@dimen/dimens_30dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/username_layout"
style="@style/ActionButtonStyle" />

<Button
android:id="@+id/cancel_attributes"
android:text="@string/cancel_message"
android:layout_marginTop="@dimen/dimens_15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/submit_attributes"
style="@style/ActionButtonStyle" />

</androidx.constraintlayout.widget.ConstraintLayout>
4 changes: 4 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
<string name="hint_text_value">"Please fill in the required information:"</string>
<string name="hint_password_text_value">"Please submit a new password."</string>
<string name="oob_hint_text_value">"Please enter the code that you received in your email."</string>
<string name="attributes_hint_text_value">"Additional attributes are required, please enter them below."</string>
<string name="mfa_challenge_hint_text_value">"Please enter the challenge that you received in your challengeChannel loginHint."</string>
<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>
<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>
Expand Down Expand Up @@ -64,13 +65,16 @@

<!-- Fields -->
<string name="attribute_email">"Email"<font color='@android:color/red'>*</font></string>
<string name="attribute_email_or_username">"Email or Username"<font color='@android:color/red'>*</font></string>
<string name="attribute_phone_number">"Phone number"<font color='@android:color/red'>*</font></string>
<string name="attribute_password">"Password"<font color='@android:color/red'>*</font></string>
<string name="attribute_country">"Country"</string>
<string name="attribute_city">"City"</string>
<string name="attribute_given">"Given name"</string>
<string name="attribute_surname">"Surname"</string>
<string name="attribute_otp">"Code"</string>
<string name="attribute_username">"Username"</string>
<string name="submit_attributes">"Submit"</string>
<string name="attribute_mfa_challenge">"Challenge"</string>

<!-- Results -->
Expand Down
4 changes: 2 additions & 2 deletions gradle/versions.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ ext {
compileSdkVersion = 34
buildToolsVersion = "28.0.3"
coreKtxVersion = "1.6.0"
kotlinXCoroutinesVersion = "1.6.4"
kotlinVersion = '1.7.21'
kotlinXCoroutinesVersion = "1.7.3"
kotlinVersion = '1.9.21'

// Plugins
gradleVersion = '8.11.1'
Expand Down