Skip to content
Merged
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
2 changes: 2 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ MSAL Wiki : https://github.com/AzureAD/microsoft-authentication-library-for-andr
vNext
----------
- [MAJOR] Update proguard rules (#2372)
- [MINOR] SDK now handles SMS as strong authentication method (#2382)
- [MINOR] Awaiting MFA Delegate now automatically returns the AuthMethods to be used when calling MFA Challenge (#2380)

Version 7.1.0
----------
Expand Down
2 changes: 1 addition & 1 deletion common
Submodule common updated 20 files
+4 −0 changelog.txt
+38 −4 common/src/main/java/com/microsoft/identity/common/nativeauth/internal/controllers/NativeAuthMsalController.kt
+62 −4 common/src/test/java/com/microsoft/identity/common/nativeauth/internal/controllers/NativeAuthControllerTest.kt
+7 −1 common4j/src/main/com/microsoft/identity/common/java/flighting/CommonFlight.java
+11 −0 common4j/src/main/com/microsoft/identity/common/java/nativeauth/controllers/results/JITCommandResult.kt
+2 −2 common4j/src/main/com/microsoft/identity/common/java/nativeauth/controllers/results/SignInCommandResult.kt
+0 −2 common4j/src/main/com/microsoft/identity/common/java/nativeauth/providers/NativeAuthConstants.kt
+9 −1 ...j/src/main/com/microsoft/identity/common/java/nativeauth/providers/responses/jit/JITChallengeApiResponse.kt
+17 −0 ...n4j/src/main/com/microsoft/identity/common/java/nativeauth/providers/responses/jit/JITChallengeApiResult.kt
+1 −1 ...com/microsoft/identity/common/java/nativeauth/providers/responses/signin/AuthenticationMethodApiResponse.kt
+1 −1 ...n/com/microsoft/identity/common/java/nativeauth/providers/responses/signin/AuthenticationMethodApiResult.kt
+4 −0 common4j/src/main/com/microsoft/identity/common/java/nativeauth/util/ApiErrorResponseUtil.kt
+14 −1 common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java
+16 −0 common4j/src/main/com/microsoft/identity/common/java/opentelemetry/OTelUtility.java
+25 −0 .../main/com/microsoft/identity/common/java/providers/microsoft/azureactivedirectory/AzureActiveDirectory.java
+1 −1 .../com/microsoft/identity/common/java/providers/microsoft/azureactivedirectory/AzureActiveDirectoryCloud.java
+118 −28 common4j/src/main/com/microsoft/identity/common/java/providers/oauth2/OpenIdProviderConfigurationClient.java
+1 −0 common4j/src/test/com/microsoft/identity/common/java/nativeauth/providers/NativeAuthRequestProviderTest.kt
+101 −0 common4j/src/test/com/microsoft/identity/common/java/providers/oauth2/OpenIdProviderConfigurationClientTest.kt
+1 −0 common4j/src/testFixtures/java/com/microsoft/identity/common/nativeauth/MockApiResponseType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ import android.os.Parcel
import android.os.Parcelable
import com.microsoft.identity.common.java.nativeauth.providers.responses.signin.AuthenticationMethodApiResult
import com.microsoft.identity.common.java.nativeauth.util.ILoggable
import com.microsoft.identity.nativeauth.statemachine.states.AwaitingMFAState
import com.microsoft.identity.nativeauth.utils.serializable

/**
* AuthMethod represents a user's authentication methods.
Expand All @@ -40,9 +38,9 @@ data class AuthMethod(
val challengeType: String,

// Auth method login hint (e.g. [email protected])
val loginHint: String,
val loginHint: String?,

// Auth method challenge channel (email, etc.)
// Auth method challenge channel (email, sms, etc.)
val challengeChannel: String,
) : ILoggable, Parcelable {
override fun toUnsanitizedString(): String = "AuthMethod(id=$id, " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,7 @@ class NativeAuthPublicClientApplication(
nextState = SignInCodeRequiredState(
continuationToken = result.continuationToken,
correlationId = result.correlationId,
username = username,
scopes = scopes,
config = nativeAuthConfig,
claimsRequestJson = params.claimsRequestJson
Expand Down Expand Up @@ -719,6 +720,7 @@ class NativeAuthPublicClientApplication(
nextState = SignInPasswordRequiredState(
continuationToken = result.continuationToken,
correlationId = result.correlationId,
username = username,
scopes = scopes,
config = nativeAuthConfig,
claimsRequestJson = params.claimsRequestJson
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,10 @@ class NativeAuthChallengeAuthMethodParameters(
/**
* authentication method to challenge
*/
val authMethod: AuthMethod
) {
val authMethod: AuthMethod,

/**
* email to contact to register a new strong authentication method
* email or phone number to contact to register a new strong authentication method
*/
var verificationContact: String? = null
}
var verificationContact: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,14 @@

package com.microsoft.identity.nativeauth.statemachine.errors

import com.microsoft.identity.nativeauth.statemachine.results.GetAccessTokenResult
import com.microsoft.identity.nativeauth.statemachine.results.GetAccountResult
import com.microsoft.identity.nativeauth.statemachine.results.ResetPasswordResendCodeResult
import com.microsoft.identity.nativeauth.statemachine.results.ResetPasswordResult
import com.microsoft.identity.nativeauth.statemachine.results.ResetPasswordStartResult
import com.microsoft.identity.nativeauth.statemachine.results.ResetPasswordSubmitCodeResult
import com.microsoft.identity.nativeauth.statemachine.results.ResetPasswordSubmitPasswordResult
import com.microsoft.identity.nativeauth.statemachine.results.SignInResendCodeResult
import com.microsoft.identity.nativeauth.statemachine.results.SignInResult
import com.microsoft.identity.nativeauth.statemachine.results.SignInSubmitCodeResult
import com.microsoft.identity.nativeauth.statemachine.results.SignInSubmitPasswordResult
import com.microsoft.identity.nativeauth.statemachine.results.SignOutResult
import com.microsoft.identity.nativeauth.statemachine.results.SignUpResendCodeResult
import com.microsoft.identity.nativeauth.statemachine.results.SignUpResult
import com.microsoft.identity.nativeauth.statemachine.results.SignUpSubmitAttributesResult
import com.microsoft.identity.nativeauth.statemachine.results.SignUpSubmitCodeResult
import com.microsoft.identity.nativeauth.statemachine.results.SignUpSubmitPasswordResult

/**
* ErrorTypes class holds the possible error type values that are shared between the errors
Expand Down Expand Up @@ -89,6 +80,12 @@ internal class ErrorTypes {
*/
const val INVALID_INPUT = "invalid_input"

/*
* The VERIFICATION_CONTACT_BLOCKED value indicates the verification contact provided has been blocked.
* Try using another email or phone number, or select an alternative authentication method.
*/
const val VERIFICATION_CONTACT_BLOCKED = "verification_contact_blocked"

/*
* The INVALID_STATE value indicates a misconfigured or expired state, or an internal error
* in state transitions. If this occurs, the flow should be restarted.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ class RegisterStrongAuthChallengeError(
): BrowserRequiredError, RegisterStrongAuthChallengeResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception)
{
fun isInvalidInput(): Boolean = this.errorType == ErrorTypes.INVALID_INPUT

/*
* Returns true if the verification contact provided has been blocked.
* Try using another email or phone number, or select an alternative authentication method.
*/
fun isVerificationContactBlocked(): Boolean = this.errorType == ErrorTypes.VERIFICATION_CONTACT_BLOCKED
}

class RegisterStrongAuthSubmitChallengeError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ interface MFARequiredResult: Result {
* @param nextState [com.microsoft.identity.nativeauth.statemachine.states.MFARequiredState] the current state of the flow with follow-on methods.
* @param codeLength the length of the challenge required by the server.
* @param sentTo the email/phone number the challenge was sent to.
* @param channel the channel(email/phone) the challenge was sent through.
* @param channel the channel(email/sms) the challenge was sent through.
*/
class VerificationRequired(
override val nextState: MFARequiredState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ interface SignInResult : Result {
class MFARequired(
override val nextState: AwaitingMFAState,
val authMethods: List<AuthMethod>
) : Result.SuccessResult(nextState = nextState), SignInResult, SignInSubmitPasswordResult
) : Result.SuccessResult(nextState = nextState), SignInResult, SignInSubmitPasswordResult, SignInSubmitCodeResult

/**
* StrongAuthMethodRegistration Result, which indicates that a registration of a strong authentication method is required to continue.
Expand All @@ -95,7 +95,7 @@ interface SignInResult : Result {
class StrongAuthMethodRegistrationRequired(
override val nextState: RegisterStrongAuthState,
val authMethods: List<AuthMethod>
) : Result.SuccessResult(nextState = nextState), SignInResult, SignInSubmitPasswordResult
) : Result.SuccessResult(nextState = nextState), SignInResult, SignInSubmitPasswordResult, SignInSubmitCodeResult
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,20 @@ abstract class BaseJITSubmitChallengeState(
tag,
"Warning: this API is experimental. It may be changed in the future without notice. Do not use in production applications."
)
// if external developer does not provide a verification contact, we use the login hint
val verificationContact: String = parameters.verificationContact.takeIf { !it.isNullOrBlank() } ?: parameters.authMethod.loginHint
// Currently, only email is supported for the challengeChannel. Continuation token grant type is used only for "preverified" flow.
val challengeChannel = NativeAuthConstants.ChallengeChannel.EMAIL
if (parameters.verificationContact.isBlank()) {
return RegisterStrongAuthChallengeError(
errorType = ErrorTypes.INVALID_INPUT,
errorMessage = "Invalid verification contact",
correlationId = correlationId
)
}

val params =
CommandParametersAdapter.createJITChallengeAuthMethodCommandParameters(
config,
config.oAuth2TokenCache,
verificationContact,
challengeChannel,
parameters.verificationContact,
parameters.authMethod.challengeChannel,
parameters.authMethod.challengeType,
correlationId,
continuationToken,
Expand Down Expand Up @@ -108,6 +112,15 @@ abstract class BaseJITSubmitChallengeState(
errorCodes = result.errorCodes
)
}
is JITCommandResult.BlockedVerificationContact -> {
RegisterStrongAuthChallengeError(
errorType = ErrorTypes.VERIFICATION_CONTACT_BLOCKED,
error = result.error,
errorMessage = result.errorDescription,
correlationId = result.correlationId,
errorCodes = result.errorCodes
)
}
is JITCommandResult.VerificationRequired -> {
RegisterStrongAuthChallengeResult.VerificationRequired(
result = NativeAuthRegisterStrongAuthVerificationRequiredResultParameter(
Expand All @@ -134,6 +147,10 @@ abstract class BaseJITSubmitChallengeState(
}
}
}

private fun isChallengeChannelSMS(challengeChannel: String): Boolean {
return challengeChannel.equals(NativeAuthConstants.ChallengeChannel.SMS, ignoreCase = true)
}
}

class RegisterStrongAuthState(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ import kotlinx.coroutines.withContext
class SignInCodeRequiredState internal constructor(
override val continuationToken: String,
override val correlationId: String,
private val username: String,
private val scopes: List<String>?,
private val claimsRequestJson: String?,
private val config: NativeAuthPublicClientApplicationConfiguration
Expand All @@ -85,6 +86,7 @@ class SignInCodeRequiredState internal constructor(
constructor(parcel: Parcel) : this(
continuationToken = parcel.readString() ?: "",
correlationId = parcel.readString() ?: "UNSET",
username = parcel.readString() ?: "",
scopes = parcel.createStringArrayList(),
claimsRequestJson = parcel.readString(),
config = parcel.serializable<NativeAuthPublicClientApplicationConfiguration>() as NativeAuthPublicClientApplicationConfiguration
Expand Down Expand Up @@ -201,6 +203,27 @@ class SignInCodeRequiredState internal constructor(
exception = result.exception
)
}
is SignInCommandResult.MFARequired -> {
SignInResult.MFARequired(
nextState = AwaitingMFAState(
continuationToken = result.continuationToken,
correlationId = result.correlationId,
scopes = scopes,
config = config
),
authMethods = result.authMethods.toListOfAuthMethods()
)
}
is SignInCommandResult.StrongAuthMethodRegistrationRequired -> {
SignInResult.StrongAuthMethodRegistrationRequired(
nextState = RegisterStrongAuthState(
continuationToken = result.continuationToken,
correlationId = result.correlationId,
config = config
),
authMethods = result.authMethods.toListOfAuthMethods()
)
}
}
} catch (e: Exception) {
SubmitCodeError(
Expand Down Expand Up @@ -277,6 +300,7 @@ class SignInCodeRequiredState internal constructor(
nextState = SignInCodeRequiredState(
continuationToken = result.continuationToken,
correlationId = result.correlationId,
username = username,
scopes = scopes,
config = config,
claimsRequestJson = claimsRequestJson
Expand Down Expand Up @@ -358,6 +382,7 @@ class SignInCodeRequiredState internal constructor(
class SignInPasswordRequiredState(
override val continuationToken: String,
override val correlationId: String,
private val username: String,
private val scopes: List<String>?,
private val claimsRequestJson: String?,
private val config: NativeAuthPublicClientApplicationConfiguration
Expand All @@ -366,6 +391,7 @@ class SignInPasswordRequiredState(
constructor(parcel: Parcel) : this(
continuationToken = parcel.readString() ?: "",
correlationId = parcel.readString() ?: "UNSET",
username = parcel.readString() ?: "",
scopes = parcel.createStringArrayList(),
claimsRequestJson = parcel.readString(),
config = parcel.serializable<NativeAuthPublicClientApplicationConfiguration>() as NativeAuthPublicClientApplicationConfiguration
Expand Down Expand Up @@ -720,6 +746,17 @@ class SignInContinuationState(
authMethods = result.authMethods.toListOfAuthMethods()
)
}
is SignInCommandResult.MFARequired -> {
SignInResult.MFARequired(
nextState = AwaitingMFAState(
continuationToken = result.continuationToken,
correlationId = result.correlationId,
scopes = parameters.scopes,
config = config
),
authMethods = result.authMethods.toListOfAuthMethods()
)
}
is INativeAuthCommandResult.Redirect -> {
SignInContinuationError(
errorType = ErrorTypes.BROWSER_REQUIRED,
Expand Down
Loading