diff --git a/common b/common
index 4941a0f713..73483c83aa 160000
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit 4941a0f7131161c653f11d7f9b75a255976ea74b
+Subproject commit 73483c83aa0bc410f28f9d45556291de238d8cdf
diff --git a/msal/src/main/java/com/microsoft/identity/client/PublicClientApplication.java b/msal/src/main/java/com/microsoft/identity/client/PublicClientApplication.java
index 6bc5a50725..f0cf2b52d3 100644
--- a/msal/src/main/java/com/microsoft/identity/client/PublicClientApplication.java
+++ b/msal/src/main/java/com/microsoft/identity/client/PublicClientApplication.java
@@ -113,7 +113,6 @@
import com.microsoft.identity.common.java.commands.parameters.GenerateShrCommandParameters;
import com.microsoft.identity.common.java.commands.parameters.InteractiveTokenCommandParameters;
import com.microsoft.identity.common.java.commands.parameters.SilentTokenCommandParameters;
-import com.microsoft.identity.common.java.controllers.BaseController;
import com.microsoft.identity.common.java.controllers.CommandDispatcher;
import com.microsoft.identity.common.java.controllers.CommandResult;
import com.microsoft.identity.common.java.controllers.ExceptionAdapter;
@@ -143,6 +142,7 @@
import com.microsoft.identity.nativeauth.INativeAuthPublicClientApplication;
import com.microsoft.identity.nativeauth.NativeAuthPublicClientApplication;
import com.microsoft.identity.nativeauth.NativeAuthPublicClientApplicationConfiguration;
+import com.microsoft.identity.nativeauth.NativeAuthPublicClientApplicationParameters;
import java.io.File;
import java.util.ArrayList;
@@ -237,6 +237,7 @@ static class NONNULL_CONSTANTS {
static final String AUTHORITY = "authority";
static final String REDIRECT_URI = "redirect_uri";
static final String CONFIG_FILE = "config_file";
+ static final String CLIENT_PARAMETER = "client_parameter";
static final String ACTIVITY = "activity";
static final String SCOPES = "scopes";
static final String ACCOUNT = "account";
@@ -846,6 +847,7 @@ public static INativeAuthPublicClientApplication createNativeAuthPublicClientApp
null,
null,
null,
+ null,
null
);
} catch (MsalException e) {
@@ -897,6 +899,7 @@ public static INativeAuthPublicClientApplication createNativeAuthPublicClientApp
null,
null,
null,
+ null,
null
);
} catch (BaseException e) {
@@ -928,8 +931,12 @@ public static INativeAuthPublicClientApplication createNativeAuthPublicClientApp
* @param clientId The application client id. Cannot be null.
* @param authority The default authority to be used for the authority. If this is null, the default authority will be used.
* @param redirectUri The redirect URI of the application.
+ * @param challengeTypes The challengeTypes supported for authentication declared by client.
* @return An instance of INativeAuthPublicClientApplication.
+ *
+ * @deprecated This method is deprecated. Use createNativeAuthPublicClientApplication(Context, NativeAuthPublicClientApplicationConfiguration) instead.
*/
+ @Deprecated
public static INativeAuthPublicClientApplication createNativeAuthPublicClientApplication(
@NonNull final Context context,
@NonNull final String clientId,
@@ -947,7 +954,59 @@ public static INativeAuthPublicClientApplication createNativeAuthPublicClientApp
clientId,
authority,
redirectUri,
- challengeTypes
+ challengeTypes,
+ null
+ );
+ } catch (BaseException e) {
+ throw new MsalClientException(
+ UNKNOWN_ERROR,
+ NATIVE_AUTH_APPLICATION_CREATION_UNKNOWN_ERROR_MESSAGE,
+ e
+ );
+ }
+ }
+
+ /**
+ * Creates an instance of INativeAuthPublicClientApplication using the provided context and configuration.
+ *
+ *
{@link PublicClientApplication#createNativeAuthPublicClientApplication(Context, NativeAuthPublicClientApplicationParameters)}
+ * will read the client id and other configuration settings from the provided configuration object.
+ *
+ * This function will pass back an {@link MsalClientException} object if it is unable
+ * to return {@link INativeAuthPublicClientApplication}. For example, AccountMode
+ * in configuration is not set to single.
+ *
+ * @param context Application's {@link Context}. The SDK requires the application context
+ * to be passed in {@link PublicClientApplication}. Cannot be null.
+ *
+ * Note: The {@link Context} should be the application context instead of
+ * the running activity's context, which could potentially make the SDK hold a
+ * strong reference to the activity, thus preventing correct garbage
+ * collection and causing bugs.
+ *
+ * @param parameters The NativeAuthPublicClientApplication parameter class containing mandatory client ID, authorityUri, challenge types and optional capabilities, redirectUri.
+ * Cannot be null.
+ *
+ * For more information on the schema of the MSAL configuration object,
+ * please see Android app resource overview
+ * and MSAL Github Wiki.
+ *
+ * @return An instance of INativeAuthPublicClientApplication.
+ */
+ public static INativeAuthPublicClientApplication createNativeAuthPublicClientApplication(
+ @NonNull final Context context,
+ @NonNull final NativeAuthPublicClientApplicationParameters parameters) throws MsalException {
+ validateNonNullArgument(context, NONNULL_CONSTANTS.CONTEXT);
+ validateNonNullArgument(parameters, NONNULL_CONSTANTS.CLIENT_PARAMETER);
+
+ try {
+ return createNativeAuthApplication(
+ Companion.initializeNativeAuthConfiguration(context),
+ parameters.getClientId(),
+ parameters.getAuthorityUrl(),
+ parameters.getRedirectUri(),
+ parameters.getChallengeTypes(),
+ parameters.getCapabilities()
);
} catch (BaseException e) {
throw new MsalClientException(
@@ -1133,7 +1192,8 @@ private static NativeAuthPublicClientApplication createNativeAuthApplication(@No
@Nullable final String clientId,
@Nullable final String authority,
@Nullable final String redirectUri,
- @Nullable final List challengeTypes) throws BaseException {
+ @Nullable final List challengeTypes,
+ @Nullable final List capabilities) throws BaseException {
if (clientId != null) {
config.setClientId(clientId);
}
@@ -1154,6 +1214,10 @@ private static NativeAuthPublicClientApplication createNativeAuthApplication(@No
config.setChallengeTypes(challengeTypes);
}
+ if (capabilities != null) {
+ config.setCapabilities(capabilities);
+ }
+
// Check whether account mode is set to SINGLE
validateAccountModeConfiguration(config);
diff --git a/msal/src/main/java/com/microsoft/identity/client/exception/MsalClientException.java b/msal/src/main/java/com/microsoft/identity/client/exception/MsalClientException.java
index 99cca81f0a..dda91eabbf 100644
--- a/msal/src/main/java/com/microsoft/identity/client/exception/MsalClientException.java
+++ b/msal/src/main/java/com/microsoft/identity/client/exception/MsalClientException.java
@@ -278,7 +278,9 @@ public final class MsalClientException extends MsalException {
* Configuration error. Native auth app passed with an invalid challenge type.
*/
public static final String NATIVE_AUTH_INVALID_CHALLENGE_TYPE_ERROR_CODE = "native_auth_invalid_challenge_type";
+ public static final String NATIVE_AUTH_INVALID_CAPABILITY_ERROR_CODE = "native_auth_invalid_capability";
public static final String NATIVE_AUTH_INVALID_CHALLENGE_TYPE_ERROR_MESSAGE = "NativeAuthPublicClientApplication detected invalid challenge type.";
+ public static final String NATIVE_AUTH_INVALID_CAPABILITY_ERROR_MESSAGE = "NativeAuthPublicClientApplication detected invalid capability.";
public MsalClientException(final String errorCode) {
super(errorCode);
diff --git a/msal/src/main/java/com/microsoft/identity/client/internal/CommandParametersAdapter.java b/msal/src/main/java/com/microsoft/identity/client/internal/CommandParametersAdapter.java
index bcd31dd485..28d41e459f 100644
--- a/msal/src/main/java/com/microsoft/identity/client/internal/CommandParametersAdapter.java
+++ b/msal/src/main/java/com/microsoft/identity/client/internal/CommandParametersAdapter.java
@@ -362,6 +362,7 @@ public static SignUpStartCommandParameters createSignUpStartCommandParameters(
.username(username)
.password(password)
.challengeType(configuration.getChallengeTypes())
+ .capabilities(configuration.getCapabilities())
.userAttributes(userAttributes)
// Start of the flow, so there is no correlation ID to use from a previous API response.
// Set it to a default value.
@@ -569,6 +570,7 @@ public static SignInStartCommandParameters createSignInStartCommandParameters(
.authenticationScheme(authenticationScheme)
.clientId(configuration.getClientId())
.challengeType(configuration.getChallengeTypes())
+ .capabilities(configuration.getCapabilities())
.claimsRequestJson(claimsRequestJson)
.scopes(scopes)
// Start of the flow, so there is no correlation ID to use from a previous API response.
@@ -993,6 +995,7 @@ public static ResetPasswordStartCommandParameters createResetPasswordStartComman
.authority(authority)
.username(username)
.challengeType(configuration.getChallengeTypes())
+ .capabilities(configuration.getCapabilities())
.clientId(configuration.getClientId())
// Start of the flow, so there is no correlation ID to use from a previous API response.
// Set it to a default value.
diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationConfiguration.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationConfiguration.kt
index e2520bc399..35fd651d7e 100644
--- a/msal/src/main/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationConfiguration.kt
+++ b/msal/src/main/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationConfiguration.kt
@@ -48,10 +48,13 @@ public class NativeAuthPublicClientApplicationConfiguration :
private val TAG = NativeAuthPublicClientApplicationConfiguration::class.java.simpleName
private val VALID_CHALLENGE_TYPES = listOf(NativeAuthConstants.ChallengeType.PASSWORD,
NativeAuthConstants.ChallengeType.OOB, NativeAuthConstants.ChallengeType.REDIRECT)
+ private val VALID_CAPABILITIES = listOf(NativeAuthConstants.Capabilities.MFA_REQUIRED,
+ NativeAuthConstants.Capabilities.REGISTRATION_REQUIRED)
}
private object NativeAuthSerializedNames {
const val CHALLENGE_TYPES = "challenge_types"
+ const val CAPABILITIES = "capabilities"
const val USE_MOCK_API = "use_mock_api_for_native_auth"
const val DC = "dc"
}
@@ -61,6 +64,11 @@ public class NativeAuthPublicClientApplicationConfiguration :
@SerializedName(NativeAuthSerializedNames.CHALLENGE_TYPES)
private var challengeTypes: List? = null
+ //List of capabilities supported by the client.
+ //For a complete list of capabilities see [NativeAuthConstants.Capabilities]
+ @SerializedName(NativeAuthSerializedNames.CAPABILITIES)
+ private var capabilities: List? = null
+
//The mock API authority used for testing will be rejected by validation logic run on
// instantiation. This flag is used to bypass those checks in various points in the application
@SerializedName(NativeAuthSerializedNames.USE_MOCK_API)
@@ -79,6 +87,14 @@ public class NativeAuthPublicClientApplicationConfiguration :
this.challengeTypes = challengeTypes
}
+ fun getCapabilities(): List? {
+ return capabilities
+ }
+
+ fun setCapabilities(capabilities: List?) {
+ this.capabilities = capabilities
+ }
+
fun mergeConfiguration(config: NativeAuthPublicClientApplicationConfiguration) {
// Call super.mergeConfiguration to handle base configuration fields
super.mergeConfiguration(config)
@@ -91,6 +107,8 @@ public class NativeAuthPublicClientApplicationConfiguration :
// Handle Native Auth specific fields
challengeTypes = if (config.challengeTypes == null) challengeTypes else config.challengeTypes
+ capabilities = if (config.capabilities == null) capabilities else config.capabilities
+
useMockAuthority = if (config.useMockAuthority == null) useMockAuthority else config.useMockAuthority
dc = if (config.dc == null) dc else config.dc
@@ -174,15 +192,21 @@ public class NativeAuthPublicClientApplicationConfiguration :
// Check that challenge types are all valid
validateChallengeTypes()
+ // Check that capabilities are all valid
+ validateCapabilities()
}
/**
* Validates that the challenge types passed are valid
*/
+ @Throws(MsalClientException::class)
private fun validateChallengeTypes() {
// Make all challenge types lowercase for simplicity
challengeTypes = challengeTypes?.map { it.lowercase() }
+ // Remove duplicate capabilities
+ challengeTypes = challengeTypes?.distinct()
+
challengeTypes?.forEach { challengeType ->
// Make sure challenge types passed were valid
if (challengeType !in VALID_CHALLENGE_TYPES) {
@@ -194,6 +218,28 @@ public class NativeAuthPublicClientApplicationConfiguration :
}
}
+ /**
+ * Validates that the capabilities passed are valid
+ */
+ @Throws(MsalClientException::class)
+ private fun validateCapabilities() {
+ // Make all capabilities lowercase for simplicity
+ capabilities = capabilities?.map { it.lowercase() }
+
+ // Remove duplicate capabilities
+ capabilities = capabilities?.distinct()
+
+ capabilities?.forEach { capability ->
+ // Make sure capabilities passed were valid
+ if (capability !in VALID_CAPABILITIES) {
+ throw MsalClientException(
+ MsalClientException.NATIVE_AUTH_INVALID_CAPABILITY_ERROR_CODE,
+ MsalClientException.NATIVE_AUTH_INVALID_CAPABILITY_ERROR_MESSAGE + " \"" + capability + "\""
+ )
+ }
+ }
+ }
+
/**
* Overriding this method to add a check for redirect uri. If no uri was passed, we don't need to check this.
*/
diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationParameters.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationParameters.kt
new file mode 100644
index 0000000000..b6a36e1d69
--- /dev/null
+++ b/msal/src/main/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationParameters.kt
@@ -0,0 +1,51 @@
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package com.microsoft.identity.nativeauth
+
+
+public class NativeAuthPublicClientApplicationParameters (
+ /**
+ * The application client id. Cannot be null.
+ */
+ val clientId: String,
+ /**
+ * The authorityUrl to be used for the authority.
+ */
+ val authorityUrl: String,
+ /**
+ * The challenge types supported for authentication declared by client. Cannot be null.
+ */
+ val challengeTypes: List,
+) {
+
+ /**
+ * The capabilities supported for authentication declared by client.
+ */
+ var capabilities: List? = null
+
+ /**
+ * The redirect URI of the application. Required for using browser.
+ */
+ var redirectUri: String? = null
+}
diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/parameters/NativeAuthChallengeAuthMethodParameters.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/parameters/NativeAuthChallengeAuthMethodParameters.kt
index c8818674b2..9c1418d2be 100644
--- a/msal/src/main/java/com/microsoft/identity/nativeauth/parameters/NativeAuthChallengeAuthMethodParameters.kt
+++ b/msal/src/main/java/com/microsoft/identity/nativeauth/parameters/NativeAuthChallengeAuthMethodParameters.kt
@@ -1,3 +1,26 @@
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
package com.microsoft.identity.nativeauth.parameters
import com.microsoft.identity.nativeauth.AuthMethod
diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/parameters/NativeAuthRegisterStrongAuthVerificationRequiredResultParameters.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/parameters/NativeAuthRegisterStrongAuthVerificationRequiredResultParameters.kt
index ad46912259..96dad98307 100644
--- a/msal/src/main/java/com/microsoft/identity/nativeauth/parameters/NativeAuthRegisterStrongAuthVerificationRequiredResultParameters.kt
+++ b/msal/src/main/java/com/microsoft/identity/nativeauth/parameters/NativeAuthRegisterStrongAuthVerificationRequiredResultParameters.kt
@@ -1,3 +1,26 @@
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
package com.microsoft.identity.nativeauth.parameters
import com.microsoft.identity.nativeauth.statemachine.states.RegisterStrongAuthVerificationRequiredState
diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/Error.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/Error.kt
index 0a89c40957..c93dc23d19 100644
--- a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/Error.kt
+++ b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/Error.kt
@@ -175,7 +175,7 @@ class ResendCodeError(
override val correlationId: String,
override val errorCodes: List? = null,
override var exception: Exception? = null
-): SignInResendCodeResult, SignUpResendCodeResult, ResetPasswordResendCodeResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception)
+): BrowserRequiredError, SignInResendCodeResult, SignUpResendCodeResult, ResetPasswordResendCodeResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception)
class GetAccountError(
override val errorType: String? = null,
@@ -193,4 +193,4 @@ class SignOutError(
override val correlationId: String,
override val errorCodes: List? = null,
override var exception: Exception? = null
-): SignOutResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception)
\ No newline at end of file
+): SignOutResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception)
diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/GetAccessTokenError.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/GetAccessTokenError.kt
index aabb96ba4a..91785debe4 100644
--- a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/GetAccessTokenError.kt
+++ b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/GetAccessTokenError.kt
@@ -48,7 +48,7 @@ class GetAccessTokenError(
override val correlationId: String,
override val errorCodes: List? = null,
override var exception: Exception? = null
-): GetAccessTokenResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception) {
+): BrowserRequiredError, GetAccessTokenResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception) {
fun isNoAccountFound() : Boolean = this.errorType == GetAccessTokenErrorTypes.NO_ACCOUNT_FOUND
fun isInvalidScopes(): Boolean = this.errorType == GetAccessTokenErrorTypes.INVALID_SCOPES
diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/JITErrors.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/JITErrors.kt
index aaf7f545f4..a3c31348f7 100644
--- a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/JITErrors.kt
+++ b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/JITErrors.kt
@@ -10,7 +10,7 @@ class RegisterStrongAuthChallengeError(
override val correlationId: String,
override val errorCodes: List? = null,
override var exception: Exception? = null
-): RegisterStrongAuthChallengeResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception)
+): BrowserRequiredError, RegisterStrongAuthChallengeResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception)
{
fun isInvalidInput(): Boolean = this.errorType == ErrorTypes.INVALID_INPUT
}
@@ -22,7 +22,7 @@ class RegisterStrongAuthSubmitChallengeError(
override val correlationId: String,
override val errorCodes: List? = null,
override var exception: Exception? = null
-): RegisterStrongAuthSubmitChallengeResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception)
+): BrowserRequiredError, RegisterStrongAuthSubmitChallengeResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception)
{
fun isInvalidChallenge(): Boolean = this.errorType == ErrorTypes.INVALID_CHALLENGE
-}
\ No newline at end of file
+}
diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/MFAErrors.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/MFAErrors.kt
index f56795672f..b6c6a583f6 100644
--- a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/MFAErrors.kt
+++ b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/MFAErrors.kt
@@ -66,7 +66,7 @@ class MFASubmitChallengeError(
override val errorCodes: List? = null,
val subError: String? = null,
override var exception: Exception? = null
-): MFASubmitChallengeResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception)
+): BrowserRequiredError, MFASubmitChallengeResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception)
{
fun isInvalidChallenge(): Boolean = this.errorType == ErrorTypes.INVALID_CHALLENGE
-}
\ No newline at end of file
+}
diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/ResetPasswordErrors.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/ResetPasswordErrors.kt
index 3bbcf97381..417dd3558b 100644
--- a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/ResetPasswordErrors.kt
+++ b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/ResetPasswordErrors.kt
@@ -84,7 +84,7 @@ class ResetPasswordSubmitPasswordError(
override val errorCodes: List? = null,
val subError: String? = null,
override var exception: Exception? = null
-): ResetPasswordSubmitPasswordResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception) {
+): BrowserRequiredError, ResetPasswordSubmitPasswordResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception) {
fun isInvalidPassword() : Boolean = this.errorType == ErrorTypes.INVALID_PASSWORD
diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/SignInErrors.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/SignInErrors.kt
index 792a4ce574..64e6af5036 100644
--- a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/SignInErrors.kt
+++ b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/SignInErrors.kt
@@ -61,7 +61,7 @@ class SignInSubmitPasswordError(
override val correlationId: String,
override val errorCodes: List? = null,
override var exception: Exception? = null
-): SignInSubmitPasswordResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception) {
+): BrowserRequiredError, SignInSubmitPasswordResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception) {
fun isInvalidCredentials(): Boolean = this.errorType == SignInErrorTypes.INVALID_CREDENTIALS
}
@@ -77,9 +77,10 @@ class SignInSubmitPasswordError(
* @param exception an internal unexpected exception that happened.
*/
open class SignInContinuationError(
+ override val errorType: String? = null,
override val error: String? = null,
override val errorMessage: String?,
override val correlationId: String,
override val errorCodes: List? = null,
override var exception: Exception? = null
-): SignInResult, Error(errorType = null, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception)
+): BrowserRequiredError, SignInResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception)
diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/JITStates.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/JITStates.kt
index 43256ca091..8434b15082 100644
--- a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/JITStates.kt
+++ b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/JITStates.kt
@@ -122,6 +122,15 @@ abstract class BaseJITSubmitChallengeState(
)
)
}
+ is INativeAuthCommandResult.Redirect -> {
+ RegisterStrongAuthChallengeError(
+ errorType = ErrorTypes.BROWSER_REQUIRED,
+ error = result.error,
+ errorMessage = result.redirectReason,
+ correlationId = result.correlationId,
+ errorCodes = result.errorCodes
+ )
+ }
}
}
}
@@ -314,6 +323,15 @@ class RegisterStrongAuthVerificationRequiredState(
errorCodes = result.errorCodes
)
}
+ is INativeAuthCommandResult.Redirect -> {
+ RegisterStrongAuthSubmitChallengeError(
+ errorType = ErrorTypes.BROWSER_REQUIRED,
+ error = result.error,
+ errorMessage = result.redirectReason,
+ correlationId = result.correlationId,
+ errorCodes = result.errorCodes
+ )
+ }
is INativeAuthCommandResult.APIError -> {
Logger.warnWithObject(
TAG,
diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/MFAStates.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/MFAStates.kt
index ed1c4069fb..2dbcaf149a 100644
--- a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/MFAStates.kt
+++ b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/MFAStates.kt
@@ -176,7 +176,7 @@ class AwaitingMFAState(
MFARequestChallengeError(
errorType = ErrorTypes.BROWSER_REQUIRED,
error = result.error,
- errorMessage = result.errorDescription,
+ errorMessage = result.redirectReason,
correlationId = result.correlationId
)
}
@@ -323,7 +323,7 @@ class MFARequiredState(
MFAGetAuthMethodsError(
errorType = ErrorTypes.BROWSER_REQUIRED,
error = result.error,
- errorMessage = result.errorDescription,
+ errorMessage = result.redirectReason,
correlationId = result.correlationId
)
}
@@ -464,7 +464,7 @@ class MFARequiredState(
MFARequestChallengeError(
errorType = ErrorTypes.BROWSER_REQUIRED,
error = result.error,
- errorMessage = result.errorDescription,
+ errorMessage = result.redirectReason,
correlationId = result.correlationId
)
}
@@ -568,7 +568,15 @@ class MFARequiredState(
errorCodes = result.errorCodes,
subError = result.subError
)
-
+ }
+ is INativeAuthCommandResult.Redirect -> {
+ MFASubmitChallengeError(
+ errorType = ErrorTypes.BROWSER_REQUIRED,
+ error = result.error,
+ errorMessage = result.redirectReason,
+ correlationId = result.correlationId,
+ errorCodes = result.errorCodes
+ )
}
is INativeAuthCommandResult.APIError -> {
Logger.warnWithObject(
diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/ResetPasswordStates.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/ResetPasswordStates.kt
index 103ab149f9..af0f1547c8 100644
--- a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/ResetPasswordStates.kt
+++ b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/ResetPasswordStates.kt
@@ -167,7 +167,7 @@ class ResetPasswordCodeRequiredState internal constructor(
SubmitCodeError(
errorType = ErrorTypes.BROWSER_REQUIRED,
error = result.error,
- errorMessage = result.errorDescription,
+ errorMessage = result.redirectReason,
correlationId = result.correlationId
)
}
@@ -271,7 +271,16 @@ class ResetPasswordCodeRequiredState internal constructor(
)
}
- is INativeAuthCommandResult.Redirect,
+ is INativeAuthCommandResult.Redirect -> {
+ ResendCodeError(
+ errorType = ErrorTypes.BROWSER_REQUIRED,
+ error = result.error,
+ errorMessage = result.redirectReason,
+ correlationId = result.correlationId,
+ errorCodes = result.errorCodes
+ )
+ }
+
is INativeAuthCommandResult.APIError -> {
Logger.warnWithObject(
TAG,
@@ -449,6 +458,16 @@ class ResetPasswordPasswordRequiredState internal constructor(
)
}
+ is INativeAuthCommandResult.Redirect -> {
+ ResetPasswordSubmitPasswordError(
+ errorType = ErrorTypes.BROWSER_REQUIRED,
+ error = result.error,
+ errorMessage = result.redirectReason,
+ correlationId = result.correlationId,
+ errorCodes = result.errorCodes
+ )
+ }
+
is INativeAuthCommandResult.APIError -> {
Logger.warnWithObject(
TAG,
diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/SignInStates.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/SignInStates.kt
index bb7c5f5bba..8b74cc60b2 100644
--- a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/SignInStates.kt
+++ b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/SignInStates.kt
@@ -181,7 +181,7 @@ class SignInCodeRequiredState internal constructor(
SubmitCodeError(
errorType = ErrorTypes.BROWSER_REQUIRED,
error = result.error,
- errorMessage = result.errorDescription,
+ errorMessage = result.redirectReason,
correlationId = result.correlationId
)
}
@@ -287,7 +287,17 @@ class SignInCodeRequiredState internal constructor(
)
}
- is INativeAuthCommandResult.Redirect, is INativeAuthCommandResult.APIError -> {
+ is INativeAuthCommandResult.Redirect -> {
+ ResendCodeError(
+ errorType = ErrorTypes.BROWSER_REQUIRED,
+ error = result.error,
+ errorMessage = result.redirectReason,
+ correlationId = result.correlationId,
+ errorCodes = result.errorCodes
+ )
+ }
+
+ is INativeAuthCommandResult.APIError -> {
Logger.warnWithObject(
TAG,
result.correlationId,
@@ -466,7 +476,18 @@ class SignInPasswordRequiredState(
)
)
}
- is INativeAuthCommandResult.Redirect, is INativeAuthCommandResult.APIError -> {
+
+ is INativeAuthCommandResult.Redirect -> {
+ SignInSubmitPasswordError(
+ errorType = ErrorTypes.BROWSER_REQUIRED,
+ error = result.error,
+ errorMessage = result.redirectReason,
+ correlationId = result.correlationId,
+ errorCodes = result.errorCodes
+ )
+ }
+
+ is INativeAuthCommandResult.APIError -> {
Logger.warnWithObject(
TAG,
result.correlationId,
@@ -698,7 +719,15 @@ class SignInContinuationState(
authMethods = result.authMethods.toListOfAuthMethods()
)
}
- is INativeAuthCommandResult.Redirect,
+ is INativeAuthCommandResult.Redirect -> {
+ SignInContinuationError(
+ errorType = ErrorTypes.BROWSER_REQUIRED,
+ error = result.error,
+ errorMessage = result.redirectReason,
+ correlationId = result.correlationId,
+ errorCodes = result.errorCodes
+ )
+ }
is INativeAuthCommandResult.APIError -> {
Logger.warnWithObject(
TAG,
diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/SignUpStates.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/SignUpStates.kt
index 3270b49e51..0e97b55cd3 100644
--- a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/SignUpStates.kt
+++ b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/SignUpStates.kt
@@ -198,7 +198,7 @@ class SignUpCodeRequiredState internal constructor(
SubmitCodeError(
errorType = ErrorTypes.BROWSER_REQUIRED,
error = result.error,
- errorMessage = result.errorDescription,
+ errorMessage = result.redirectReason,
correlationId = result.correlationId
)
}
@@ -313,8 +313,16 @@ class SignUpCodeRequiredState internal constructor(
channel = result.challengeChannel
)
}
-
- is INativeAuthCommandResult.Redirect, is INativeAuthCommandResult.APIError -> {
+ is INativeAuthCommandResult.Redirect -> {
+ ResendCodeError(
+ errorType = ErrorTypes.BROWSER_REQUIRED,
+ error = result.error,
+ errorMessage = result.redirectReason,
+ correlationId = result.correlationId,
+ errorCodes = result.errorCodes
+ )
+ }
+ is INativeAuthCommandResult.APIError -> {
Logger.warnWithObject(
TAG,
result.correlationId,
@@ -487,7 +495,7 @@ class SignUpPasswordRequiredState internal constructor(
SignUpSubmitPasswordError(
errorType = ErrorTypes.BROWSER_REQUIRED,
error = result.error,
- errorMessage = result.errorDescription,
+ errorMessage = result.redirectReason,
correlationId = result.correlationId
)
}
@@ -695,7 +703,7 @@ class SignUpAttributesRequiredState internal constructor(
SignUpSubmitAttributesError(
errorType = ErrorTypes.BROWSER_REQUIRED,
error = result.error,
- errorMessage = result.errorDescription,
+ errorMessage = result.redirectReason,
correlationId = result.correlationId
)
}
diff --git a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/GetTokenTests.kt b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/GetTokenTests.kt
index a088841e09..07bf0c05f5 100644
--- a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/GetTokenTests.kt
+++ b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/GetTokenTests.kt
@@ -50,11 +50,12 @@ class GetTokenTests : NativeAuthPublicClientApplicationAbstractTest() {
private val defaultConfigType = ConfigType.SIGN_IN_PASSWORD
private val defaultChallengeTypes = listOf("password", "oob")
+ private val defaultCapabilities = listOf("mfa_required", "registration_required")
override fun setup() {
super.setup()
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
resources = config.resources
}
diff --git a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/NativeAuthPublicClientApplicationAbstractTest.kt b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/NativeAuthPublicClientApplicationAbstractTest.kt
index 229e0487d1..63ae2ad800 100644
--- a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/NativeAuthPublicClientApplicationAbstractTest.kt
+++ b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/NativeAuthPublicClientApplicationAbstractTest.kt
@@ -40,6 +40,7 @@ import com.microsoft.identity.internal.testutils.labutils.LabUserQuery
import com.microsoft.identity.internal.testutils.nativeauth.ConfigType
import com.microsoft.identity.internal.testutils.nativeauth.api.models.NativeAuthTestConfig
import com.microsoft.identity.nativeauth.INativeAuthPublicClientApplication
+import com.microsoft.identity.nativeauth.NativeAuthPublicClientApplicationParameters
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.setMain
@@ -114,15 +115,19 @@ abstract class NativeAuthPublicClientApplicationAbstractTest : IPublicClientAppl
?: throw IllegalStateException("Config not $secretValue")
}
- fun setupPCA(config: NativeAuthTestConfig.Config, challengeTypes: List): INativeAuthPublicClientApplication {
+ fun setupPCA(config: NativeAuthTestConfig.Config, challengeTypes: List, capabilities: List): INativeAuthPublicClientApplication {
return try {
- PublicClientApplication.createNativeAuthPublicClientApplication(
- context,
+ val parameters = NativeAuthPublicClientApplicationParameters(
config.clientId,
config.authorityUrl,
- null,
challengeTypes
)
+ parameters.capabilities = capabilities
+
+ PublicClientApplication.createNativeAuthPublicClientApplication(
+ context,
+ parameters
+ )
} catch (e: MsalException) {
Assert.fail(e.message)
throw e
diff --git a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SSPRTest.kt b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SSPRTest.kt
index 5389e39855..4d26af3e85 100644
--- a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SSPRTest.kt
+++ b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SSPRTest.kt
@@ -49,6 +49,7 @@ class SSPRTest : NativeAuthPublicClientApplicationAbstractTest() {
private val defaultConfigType = ConfigType.SSPR
private val defaultChallengeTypes = listOf("password", "oob")
+ private val defaultCapabilities = listOf("mfa_required", "registration_required")
/**
* Verify email with email OTP first and then reset password.
@@ -58,7 +59,7 @@ class SSPRTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testSSPRSuccess() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
var result: ResetPasswordStartResult
@@ -88,7 +89,7 @@ class SSPRTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testErrorInvalidPasswordFormat() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
var result: ResetPasswordStartResult
@@ -119,7 +120,7 @@ class SSPRTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testResendCode() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
var result: ResetPasswordStartResult
@@ -155,7 +156,7 @@ class SSPRTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testErrorUserNotExist() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
runBlocking {
val username = tempEmailApi.generateRandomEmailAddressLocally()
@@ -173,7 +174,7 @@ class SSPRTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testErrorInsufficientChallengesBrowserRequired() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, listOf("password"))
+ application = setupPCA(config, listOf("password"), defaultCapabilities)
runBlocking {
val username = config.email
@@ -191,7 +192,7 @@ class SSPRTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testErrorNoPasswordLinked() {
config = getConfig(ConfigType.SIGN_IN_OTP)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes,defaultCapabilities)
runBlocking {
val username = config.email
@@ -210,7 +211,7 @@ class SSPRTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testErrorUserExistAsSocial() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
runBlocking {
val username = INVALID_EMAIL // TODO: Use social accounts instead when ready
diff --git a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignInEmailOTPTest.kt b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignInEmailOTPTest.kt
index 4a88444bce..5db7671f5c 100644
--- a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignInEmailOTPTest.kt
+++ b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignInEmailOTPTest.kt
@@ -47,12 +47,7 @@ class SignInEmailOTPTest : NativeAuthPublicClientApplicationAbstractTest() {
private val defaultConfigType = ConfigType.SIGN_IN_OTP
private val defaultChallengeTypes = listOf("password", "oob")
-
- override fun setup() {
- super.setup()
- config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
- }
+ private val defaultCapabilities = listOf("mfa_required", "registration_required")
/**
* Use valid email and OTP to get token and sign in.
@@ -62,7 +57,7 @@ class SignInEmailOTPTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testSuccess() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
retryOperation {
runBlocking {
@@ -84,7 +79,7 @@ class SignInEmailOTPTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testErrorIsUserNotFound() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
retryOperation {
runBlocking {
@@ -104,7 +99,7 @@ class SignInEmailOTPTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testErrorPasswordConfigBrowserRequired() {
config = getConfig(ConfigType.SIGN_IN_PASSWORD)
- application = setupPCA(config, listOf("oob"))
+ application = setupPCA(config, listOf("oob"), defaultCapabilities)
runBlocking {
val user = config.email
@@ -122,7 +117,7 @@ class SignInEmailOTPTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testSuccessConfigPasswordRequired() {
config = getConfig(ConfigType.SIGN_IN_PASSWORD)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
runBlocking {
val user = config.email
@@ -145,7 +140,7 @@ class SignInEmailOTPTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testResendCode() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
retryOperation {
runBlocking {
@@ -190,7 +185,7 @@ class SignInEmailOTPTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testErrorIsInvalidCode() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
runBlocking {
val user = config.email
diff --git a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignInEmailPasswordTest.kt b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignInEmailPasswordTest.kt
index ec75a1996d..17f5191312 100644
--- a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignInEmailPasswordTest.kt
+++ b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignInEmailPasswordTest.kt
@@ -47,6 +47,7 @@ class SignInEmailPasswordTest : NativeAuthPublicClientApplicationAbstractTest()
private val defaultConfigType = ConfigType.SIGN_IN_PASSWORD
private val defaultChallengeTypes = listOf("password", "oob")
+ private val defaultCapabilities = listOf("mfa_required", "registration_required")
/**
* Use valid email and password to get token.
@@ -55,7 +56,7 @@ class SignInEmailPasswordTest : NativeAuthPublicClientApplicationAbstractTest()
@Test
fun testSuccess() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
runBlocking {
val username = config.email
@@ -73,7 +74,7 @@ class SignInEmailPasswordTest : NativeAuthPublicClientApplicationAbstractTest()
@Test
fun testErrorIsUserNotFound() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
runBlocking {
val username = INVALID_EMAIL
@@ -92,7 +93,7 @@ class SignInEmailPasswordTest : NativeAuthPublicClientApplicationAbstractTest()
@Test
fun testErrorIsInvalidCredentials() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
runBlocking {
val username = config.email
@@ -111,7 +112,7 @@ class SignInEmailPasswordTest : NativeAuthPublicClientApplicationAbstractTest()
@Test
fun testErrorOutOfPersistence() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
runBlocking {
val username = config.email
@@ -137,7 +138,7 @@ class SignInEmailPasswordTest : NativeAuthPublicClientApplicationAbstractTest()
@Test
fun testErrorOutOfPersistenceDifferentAccount() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
runBlocking {
val param = NativeAuthSignInParameters(username = config.email)
@@ -177,7 +178,7 @@ class SignInEmailPasswordTest : NativeAuthPublicClientApplicationAbstractTest()
@Test
fun testSuccessOTPConfigCodeRequired() {
config = getConfig(ConfigType.SIGN_IN_OTP)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
runBlocking {
val param = NativeAuthSignInParameters(username = config.email)
@@ -203,7 +204,7 @@ class SignInEmailPasswordTest : NativeAuthPublicClientApplicationAbstractTest()
@Test
fun testErrorOTPConfigBrowserRequired() {
config = getConfig(ConfigType.SIGN_IN_OTP)
- application = setupPCA(config, listOf("password"))
+ application = setupPCA(config, listOf("password"), defaultCapabilities)
runBlocking {
val param = NativeAuthSignInParameters(username = config.email)
@@ -221,7 +222,7 @@ class SignInEmailPasswordTest : NativeAuthPublicClientApplicationAbstractTest()
@Test
fun testSignOut() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
runBlocking {
val param = NativeAuthSignInParameters(username = config.email)
diff --git a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignInJITTest.kt b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignInJITTest.kt
index 8a5ed0e747..5a4233e8bd 100644
--- a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignInJITTest.kt
+++ b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignInJITTest.kt
@@ -35,6 +35,7 @@ import com.microsoft.identity.nativeauth.parameters.NativeAuthSignInContinuation
import com.microsoft.identity.nativeauth.parameters.NativeAuthSignInParameters
import com.microsoft.identity.nativeauth.parameters.NativeAuthSignUpParameters
import com.microsoft.identity.nativeauth.statemachine.errors.MFASubmitChallengeError
+import com.microsoft.identity.nativeauth.statemachine.errors.SignInError
import com.microsoft.identity.nativeauth.statemachine.results.GetAccessTokenResult
import com.microsoft.identity.nativeauth.statemachine.results.MFARequiredResult
import com.microsoft.identity.nativeauth.statemachine.results.RegisterStrongAuthChallengeResult
@@ -66,6 +67,7 @@ class SignInJITTest : NativeAuthPublicClientApplicationAbstractTest() {
private val defaultConfigType = ConfigType.SIGN_IN_MFA_SINGLE_AUTH
private val defaultChallengeTypes = listOf("password", "oob")
+ private val defaultCapabilities = listOf("mfa_required", "registration_required")
/**
* Full flow: Ensure JIT is triggered on first signIn
@@ -81,7 +83,7 @@ class SignInJITTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun `test sign in specifying custom verification contact`() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
resources = config.resources
val authenticationContextId = "c4"
val authenticationContextRequestClaimJson = "{\"access_token\":{\"acrs\":{\"essential\":true,\"value\":\"$authenticationContextId\"}}}"
@@ -142,7 +144,7 @@ class SignInJITTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun `test sign after sign up without specify verification contact`() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
resources = config.resources
val authenticationContextId = "c4"
val authenticationContextRequestClaimJson = "{\"access_token\":{\"acrs\":{\"essential\":true,\"value\":\"$authenticationContextId\"}}}"
@@ -199,7 +201,7 @@ class SignInJITTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun `test sign after sign up with specify verification contact`() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
resources = config.resources
val authenticationContextId = "c4"
val authenticationContextRequestClaimJson = "{\"access_token\":{\"acrs\":{\"essential\":true,\"value\":\"$authenticationContextId\"}}}"
@@ -245,4 +247,49 @@ class SignInJITTest : NativeAuthPublicClientApplicationAbstractTest() {
}
}
}
+
+ /**
+ * Full flow: Ensure that correct error is returned when an user doesn’t supply the “mfa_required registration_required” capabilities
+ * - Initialise client with insufficient capabilities config
+ * - SignUp a new user with username and password
+ * - SignIn specifying authentication context as claim
+ * - Check that JIT flow is triggered
+ * - Do not specify a verification contact
+ * - Return redirect with reason registration required was not supplied
+ *
+ */
+ @Ignore("Backward compatibility feature not available in eSTS production")
+ @Test
+ fun `test Redirect is triggered when Capabilities insufficient`() {
+ config = getConfig(defaultConfigType)
+ // Initialise client with insufficient capabilities config
+ application = setupPCA(config, defaultChallengeTypes, listOf("mfa_required"))
+ resources = config.resources
+ val authenticationContextId = "c4"
+ val authenticationContextRequestClaimJson = "{\"access_token\":{\"acrs\":{\"essential\":true,\"value\":\"$authenticationContextId\"}}}"
+
+ retryOperation {
+ runBlocking {
+ // SignUp a new user with username and password
+ val username = tempEmailApi.generateRandomEmailAddressLocally()
+ val signUpParams = NativeAuthSignUpParameters(username)
+ signUpParams.password = getSafePassword().toCharArray()
+ val signUpResult = application.signUp(signUpParams)
+ assertResult(signUpResult)
+ val otp1 = tempEmailApi.retrieveCodeFromInbox(username)
+ val submitCodeResult = (signUpResult as SignUpResult.CodeRequired).nextState.submitCode(otp1)
+ assertResult(submitCodeResult)
+
+ // SignIn after signUp with authentication context as claims to trigger MFA
+ val continuationParameters = NativeAuthSignInContinuationParameters()
+ continuationParameters.claimsRequest = ClaimsRequest.getClaimsRequestFromJsonString(authenticationContextRequestClaimJson)
+ val signWithContinuationResult = (submitCodeResult as SignUpResult.Complete).nextState.signIn(continuationParameters)
+
+ // Return redirect with reason registration required was not supplied
+ assertTrue(signWithContinuationResult is SignInError)
+ assertTrue((signWithContinuationResult as SignInError).isBrowserRequired())
+ assertTrue(signWithContinuationResult.errorMessage!!.contains("registration required was not supplied"))
+ }
+ }
+ }
}
diff --git a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignInMFATest.kt b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignInMFATest.kt
index cf49063e6c..8847577d2a 100644
--- a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignInMFATest.kt
+++ b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignInMFATest.kt
@@ -32,10 +32,14 @@ import com.microsoft.identity.nativeauth.INativeAuthPublicClientApplication
import com.microsoft.identity.nativeauth.parameters.NativeAuthGetAccessTokenParameters
import com.microsoft.identity.nativeauth.parameters.NativeAuthSignInParameters
import com.microsoft.identity.nativeauth.statemachine.errors.MFASubmitChallengeError
+import com.microsoft.identity.nativeauth.statemachine.errors.ResetPasswordError
+import com.microsoft.identity.nativeauth.statemachine.errors.SignInError
+import com.microsoft.identity.nativeauth.statemachine.errors.SignUpError
import com.microsoft.identity.nativeauth.statemachine.results.GetAccessTokenResult
import com.microsoft.identity.nativeauth.statemachine.results.MFARequiredResult
import com.microsoft.identity.nativeauth.statemachine.results.SignInResult
import kotlinx.coroutines.runBlocking
+import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
@@ -55,6 +59,7 @@ class SignInMFATest : NativeAuthPublicClientApplicationAbstractTest() {
private val defaultConfigType = ConfigType.SIGN_IN_MFA_SINGLE_AUTH
private val defaultChallengeTypes = listOf("password", "oob")
+ private val defaultCapabilities = listOf("mfa_required", "registration_required")
/**
* Full flow:
@@ -72,7 +77,7 @@ class SignInMFATest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun `test submit invalid challenge, request new challenge, submit correct challenge and complete MFA flow`() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
resources = config.resources
retryOperation {
@@ -142,7 +147,7 @@ class SignInMFATest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun `test get other auth methods, request challenge on specific auth method and complete MFA flow`() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
resources = config.resources
retryOperation {
@@ -214,7 +219,7 @@ class SignInMFATest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun `test selection required, request challenge on specific auth method and complete MFA flow`() {
config = getConfig(ConfigType.SIGN_IN_MFA_MULTI_AUTH)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
resources = config.resources
retryOperation {
@@ -277,7 +282,7 @@ class SignInMFATest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun `test MFA flow is triggered when authentication context is used as claim`() {
config = getConfig(ConfigType.SIGN_IN_MFA_SINGLE_AUTH)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
resources = config.resources
val authenticationContextId = "c4"
val authenticationContextRequestClaimJson = "{\"access_token\":{\"acrs\":{\"essential\":true,\"value\":\"$authenticationContextId\"}}}"
@@ -331,4 +336,43 @@ class SignInMFATest : NativeAuthPublicClientApplicationAbstractTest() {
}
}
}
+
+ /**
+ * Full flow: Ensure that correct error is returned when an user doesn’t supply the “mfa_required” capability.
+ * - Initialise client with empty capabilities config
+ * - SignIn specifying authentication context as claim
+ * - Receive MFA required error from API
+ * - Request default challenge and submit correct challenge
+ * - Return redirect with reason mfa required was not supplied
+ *
+ */
+ @Ignore("Backward compatibility feature not available in eSTS production")
+ @Test
+ fun `test Redirect is triggered when Capabilities incapable`() {
+ config = getConfig(ConfigType.SIGN_IN_MFA_MULTI_AUTH)
+ //Initialise client with empty capabilities config
+ application = setupPCA(config, defaultChallengeTypes, listOf())
+ resources = config.resources
+ val authenticationContextId = "c4"
+ val authenticationContextRequestClaimJson = "{\"access_token\":{\"acrs\":{\"essential\":true,\"value\":\"$authenticationContextId\"}}}"
+
+ retryOperation {
+ runBlocking {
+ val username = config.email
+ val password = getSafePassword()
+ val params = NativeAuthSignInParameters(username)
+ params.password = password.toCharArray()
+ params.claimsRequest = ClaimsRequest.getClaimsRequestFromJsonString(authenticationContextRequestClaimJson)
+
+ // SignIn specifying authentication context as claim
+ val result = application.signIn(params)
+ assertResult(result)
+
+ // Return redirect with reason mfa required was not supplied
+ assertTrue(result is SignInError)
+ assertTrue((result as SignInError).isBrowserRequired())
+ assertTrue(result.errorMessage!!.contains("mfa required was not supplied"))
+ }
+ }
+ }
}
diff --git a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignUpEmailOTPAttributesTest.kt b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignUpEmailOTPAttributesTest.kt
index 0f37859966..bd471cf9b2 100644
--- a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignUpEmailOTPAttributesTest.kt
+++ b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignUpEmailOTPAttributesTest.kt
@@ -46,11 +46,13 @@ class SignUpEmailOTPAttributesTest : NativeAuthPublicClientApplicationAbstractTe
private val defaultConfigType = ConfigType.SIGN_UP_OTP_ATTRIBUTES
private val defaultChallengeTypes = listOf("password", "oob")
+ private val defaultCapabilities = listOf("mfa_required", "registration_required")
+
override fun setup() {
super.setup()
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
}
/**
diff --git a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignUpEmailOTPTest.kt b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignUpEmailOTPTest.kt
index 34df602588..1c52142755 100644
--- a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignUpEmailOTPTest.kt
+++ b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignUpEmailOTPTest.kt
@@ -47,6 +47,7 @@ class SignUpEmailOTPTest : NativeAuthPublicClientApplicationAbstractTest() {
private val defaultConfigType = ConfigType.SIGN_UP_OTP
private val defaultChallengeTypes = listOf("password", "oob")
+ private val defaultCapabilities = listOf("mfa_required", "registration_required")
/**
* Sign up with email + OTP. Verify email address using email OTP and sign up.
@@ -56,7 +57,7 @@ class SignUpEmailOTPTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testSuccess() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
retryOperation {
runBlocking { // Running with runBlocking to avoid default 10 second execution timeout.
@@ -80,7 +81,7 @@ class SignUpEmailOTPTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testResendCode() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
retryOperation {
runBlocking { // Running with runBlocking to avoid default 10 second execution timeout.
@@ -105,7 +106,7 @@ class SignUpEmailOTPTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testErrorUserExistAsOTP() {
config = getConfig(ConfigType.SIGN_IN_OTP)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
runBlocking { // Running with runBlocking to avoid default 10 second execution timeout.
val user = config.email
@@ -122,7 +123,7 @@ class SignUpEmailOTPTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testErrorUserExistAsPassword() {
config = getConfig(ConfigType.SIGN_IN_PASSWORD)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
runBlocking { // Running with runBlocking to avoid default 10 second execution timeout.
val user = config.email
@@ -141,7 +142,7 @@ class SignUpEmailOTPTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testErrorUserExistAsSocial() {
config = getConfig(ConfigType.SIGN_IN_OTP)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
runBlocking { // Running with runBlocking to avoid default 10 second execution timeout.
val user = config.email
@@ -159,7 +160,7 @@ class SignUpEmailOTPTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testErrorInvalidEmailFormat() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
runBlocking { // Running with runBlocking to avoid default 10 second execution timeout.
val user = INVALID_EMAIL
@@ -178,7 +179,7 @@ class SignUpEmailOTPTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testSignInAfterSignUp() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
retryOperation {
runBlocking { // Running with runBlocking to avoid default 10 second execution timeout.
@@ -203,7 +204,7 @@ class SignUpEmailOTPTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testErrorRedirect() {
config = getConfig(ConfigType.SIGN_UP_PASSWORD)
- application = setupPCA(config, listOf("oob"))
+ application = setupPCA(config, listOf("oob"), defaultCapabilities)
runBlocking {
val user = tempEmailApi.generateRandomEmailAddressLocally()
@@ -222,7 +223,7 @@ class SignUpEmailOTPTest : NativeAuthPublicClientApplicationAbstractTest() {
@Test
fun testPasswordRequired() {
config = getConfig(ConfigType.SIGN_UP_PASSWORD)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultChallengeTypes)
retryOperation {
runBlocking { // Running with runBlocking to avoid default 10 second execution timeout.
diff --git a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignUpEmailPasswordAttributesTest.kt b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignUpEmailPasswordAttributesTest.kt
index 6ee55c30f6..5a6b3c7cd9 100644
--- a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignUpEmailPasswordAttributesTest.kt
+++ b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignUpEmailPasswordAttributesTest.kt
@@ -47,11 +47,12 @@ class SignUpEmailPasswordAttributesTest : NativeAuthPublicClientApplicationAbstr
private val defaultConfigType = ConfigType.SIGN_UP_PASSWORD_ATTRIBUTES
private val defaultChallengeTypes = listOf("password", "oob")
+ private val defaultCapabilities = listOf("mfa_required", "registration_required")
override fun setup() {
super.setup()
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
}
/**
diff --git a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignUpEmailPasswordTest.kt b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignUpEmailPasswordTest.kt
index da60971540..5115b8c765 100644
--- a/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignUpEmailPasswordTest.kt
+++ b/msal/src/test/java/com/microsoft/identity/client/e2e/tests/network/nativeauth/SignUpEmailPasswordTest.kt
@@ -47,13 +47,14 @@ class SignUpEmailPasswordTest : NativeAuthPublicClientApplicationAbstractTest()
private val defaultConfigType = ConfigType.SIGN_UP_PASSWORD
private val defaultChallengeTypes = listOf("password", "oob")
+ private val defaultCapabilities = listOf("mfa_required", "registration_required")
@Ignore("Retrieving OTP code failure.")
@Test
fun testSignUpErrorSimple() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
retryOperation {
runBlocking {
@@ -75,7 +76,7 @@ class SignUpEmailPasswordTest : NativeAuthPublicClientApplicationAbstractTest()
@Test
fun testSuccessOTPLast() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
retryOperation {
runBlocking {
@@ -100,7 +101,7 @@ class SignUpEmailPasswordTest : NativeAuthPublicClientApplicationAbstractTest()
@Test
fun testResendEmailOOB() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
retryOperation {
runBlocking {
@@ -127,7 +128,7 @@ class SignUpEmailPasswordTest : NativeAuthPublicClientApplicationAbstractTest()
@Test
fun testSuccessOTPFirst() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
retryOperation {
runBlocking { // Running with runBlocking to avoid default 10 second execution timeout.
@@ -154,7 +155,7 @@ class SignUpEmailPasswordTest : NativeAuthPublicClientApplicationAbstractTest()
@Test
fun testSuccessOTPResend() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
retryOperation {
runBlocking {
@@ -183,7 +184,7 @@ class SignUpEmailPasswordTest : NativeAuthPublicClientApplicationAbstractTest()
@Test
fun testErrorUserExistAsPassword() {
config = getConfig(ConfigType.SIGN_IN_PASSWORD)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
runBlocking { // Running with runBlocking to avoid default 10 second execution timeout.
val user = config.email
@@ -203,7 +204,7 @@ class SignUpEmailPasswordTest : NativeAuthPublicClientApplicationAbstractTest()
@Test
fun testErrorUserExistAsSocial() {
config = getConfig(ConfigType.SIGN_IN_PASSWORD)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
runBlocking {
val user = config.email
@@ -222,7 +223,7 @@ class SignUpEmailPasswordTest : NativeAuthPublicClientApplicationAbstractTest()
@Test
fun testErrorInvalidEmailFormat() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
runBlocking {
val user = INVALID_EMAIL
@@ -242,7 +243,7 @@ class SignUpEmailPasswordTest : NativeAuthPublicClientApplicationAbstractTest()
@Test
fun testErrorInvalidPasswordFormat() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
runBlocking { // Running with runBlocking to avoid default 10 second execution timeout.
val user = tempEmailApi.generateRandomEmailAddressLocally()
@@ -262,7 +263,7 @@ class SignUpEmailPasswordTest : NativeAuthPublicClientApplicationAbstractTest()
@Test
fun testSignInAfterSignUp() {
config = getConfig(defaultConfigType)
- application = setupPCA(config, defaultChallengeTypes)
+ application = setupPCA(config, defaultChallengeTypes, defaultCapabilities)
retryOperation {
runBlocking { // Running with runBlocking to avoid default 10 second execution timeout.
diff --git a/msal/src/test/java/com/microsoft/identity/nativeauth/NativeAuthOAuth2ConfigurationTest.kt b/msal/src/test/java/com/microsoft/identity/nativeauth/NativeAuthOAuth2ConfigurationTest.kt
index 3380ad93fb..920fef4ac0 100644
--- a/msal/src/test/java/com/microsoft/identity/nativeauth/NativeAuthOAuth2ConfigurationTest.kt
+++ b/msal/src/test/java/com/microsoft/identity/nativeauth/NativeAuthOAuth2ConfigurationTest.kt
@@ -42,7 +42,8 @@ class NativeAuthOAuth2ConfigurationTest {
authorityUrl = authorityUrl,
clientId = "1234",
challengeType = "oob password redirect",
- useMockApiForNativeAuth = false
+ useMockApiForNativeAuth = false,
+ capabilities = null
)
val signUpEndpoint = configuration.getSignUpStartEndpoint()
@@ -59,7 +60,8 @@ class NativeAuthOAuth2ConfigurationTest {
authorityUrl = authorityUrl,
clientId = "1234",
challengeType = "oob password redirect",
- useMockApiForNativeAuth = false
+ useMockApiForNativeAuth = false,
+ capabilities = null
)
val signUpEndpoint = configuration.getSignUpStartEndpoint()
diff --git a/msal/src/test/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationConfigurationTest.kt b/msal/src/test/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationConfigurationTest.kt
index 45f0ef197e..22c4b20694 100644
--- a/msal/src/test/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationConfigurationTest.kt
+++ b/msal/src/test/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationConfigurationTest.kt
@@ -29,6 +29,7 @@ import com.microsoft.identity.common.java.authorities.AzureActiveDirectoryB2CAut
import com.microsoft.identity.common.java.nativeauth.authorities.NativeAuthCIAMAuthority
import junit.framework.Assert.assertEquals
import junit.framework.Assert.fail
+import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.spy
@@ -289,5 +290,100 @@ class NativeAuthPublicClientApplicationConfigurationTest {
whenever(spyConfig.useBroker).thenReturn(false)
spyConfig.setChallengeTypes(listOf("oob", "oob", "password", "redirect", "redirect", "redirect"))
spyConfig.validateConfiguration()
+ Assert.assertEquals(spyConfig.getChallengeTypes()?.size,3) // redirect added by default
+ }
+
+ // Capabilities are optional, so no exception should be thrown
+ @Test
+ fun testMissingCapabilities() {
+ val config = NativeAuthPublicClientApplicationConfiguration()
+ config.clientId = clientId
+ config.accountMode = AccountMode.SINGLE
+ val spyConfig = spy(config)
+ whenever(spyConfig.authorities).thenReturn(listOf(NativeAuthCIAMAuthority(ciamAuthority, clientId)))
+ whenever(spyConfig.defaultAuthority).thenReturn(NativeAuthCIAMAuthority(ciamAuthority, clientId))
+ whenever(spyConfig.isSharedDevice).thenReturn(false)
+ whenever(spyConfig.useBroker).thenReturn(false)
+ whenever(spyConfig.getCapabilities()).thenReturn(emptyList())
+ spyConfig.validateConfiguration()
+ }
+
+ @Test
+ fun testInvalidCapabilities() {
+ val config = NativeAuthPublicClientApplicationConfiguration()
+ config.clientId = clientId
+ config.accountMode = AccountMode.SINGLE
+ val spyConfig = spy(config)
+ whenever(spyConfig.authorities).thenReturn(listOf(NativeAuthCIAMAuthority(ciamAuthority, clientId)))
+ whenever(spyConfig.defaultAuthority).thenReturn(NativeAuthCIAMAuthority(ciamAuthority, clientId))
+ whenever(spyConfig.isSharedDevice).thenReturn(false)
+ whenever(spyConfig.useBroker).thenReturn(false)
+ spyConfig.setCapabilities(listOf("lorem"))
+
+ try {
+ spyConfig.validateConfiguration()
+ } catch (e: MsalClientException) {
+ assertEquals(MsalClientException.NATIVE_AUTH_INVALID_CAPABILITY_ERROR_CODE, e.errorCode)
+ return
+ }
+ // An exception should be thrown
+ fail()
+ }
+
+ @Test
+ fun testCaseInsensitiveCapabilities() {
+ val config = NativeAuthPublicClientApplicationConfiguration()
+ config.clientId = clientId
+ config.accountMode = AccountMode.SINGLE
+ val spyConfig = spy(config)
+ whenever(spyConfig.authorities).thenReturn(listOf(NativeAuthCIAMAuthority(ciamAuthority, clientId)))
+ whenever(spyConfig.defaultAuthority).thenReturn(NativeAuthCIAMAuthority(ciamAuthority, clientId))
+ whenever(spyConfig.isSharedDevice).thenReturn(false)
+ whenever(spyConfig.useBroker).thenReturn(false)
+ spyConfig.setCapabilities(listOf("Mfa_RequIRED"))
+ spyConfig.validateConfiguration()
+ }
+
+ @Test
+ fun testMultipleCorrectCapabilities() {
+ val config = NativeAuthPublicClientApplicationConfiguration()
+ config.clientId = clientId
+ config.accountMode = AccountMode.SINGLE
+ val spyConfig = spy(config)
+ whenever(spyConfig.authorities).thenReturn(listOf(NativeAuthCIAMAuthority(ciamAuthority, clientId)))
+ whenever(spyConfig.defaultAuthority).thenReturn(NativeAuthCIAMAuthority(ciamAuthority, clientId))
+ whenever(spyConfig.isSharedDevice).thenReturn(false)
+ whenever(spyConfig.useBroker).thenReturn(false)
+ spyConfig.setCapabilities(listOf("mfa_required", "registration_required"))
+ spyConfig.validateConfiguration()
+ }
+
+ @Test
+ fun testSingleCorrectCapability() {
+ val config = NativeAuthPublicClientApplicationConfiguration()
+ config.clientId = clientId
+ config.accountMode = AccountMode.SINGLE
+ val spyConfig = spy(config)
+ whenever(spyConfig.authorities).thenReturn(listOf(NativeAuthCIAMAuthority(ciamAuthority, clientId)))
+ whenever(spyConfig.defaultAuthority).thenReturn(NativeAuthCIAMAuthority(ciamAuthority, clientId))
+ whenever(spyConfig.isSharedDevice).thenReturn(false)
+ whenever(spyConfig.useBroker).thenReturn(false)
+ spyConfig.setCapabilities(listOf("mfa_required"))
+ spyConfig.validateConfiguration()
+ }
+
+ @Test
+ fun testRepeatedCorrectCapabilities() {
+ val config = NativeAuthPublicClientApplicationConfiguration()
+ config.clientId = clientId
+ config.accountMode = AccountMode.SINGLE
+ val spyConfig = spy(config)
+ whenever(spyConfig.authorities).thenReturn(listOf(NativeAuthCIAMAuthority(ciamAuthority, clientId)))
+ whenever(spyConfig.defaultAuthority).thenReturn(NativeAuthCIAMAuthority(ciamAuthority, clientId))
+ whenever(spyConfig.isSharedDevice).thenReturn(false)
+ whenever(spyConfig.useBroker).thenReturn(false)
+ spyConfig.setCapabilities(listOf("mfa_required", "mfa_required", "registration_required", "registration_required"))
+ spyConfig.validateConfiguration()
+ Assert.assertEquals(spyConfig.getCapabilities()?.size,2)
}
}