Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@

package com.microsoft.identity.client.e2e.tests.network.nativeauth

import com.microsoft.identity.client.claims.ClaimsRequest
import com.microsoft.identity.client.e2e.utils.assertResult
import com.microsoft.identity.internal.testutils.nativeauth.ConfigType
import com.microsoft.identity.internal.testutils.nativeauth.api.TemporaryEmailService
import com.microsoft.identity.internal.testutils.nativeauth.api.models.NativeAuthTestConfig
import com.microsoft.identity.nativeauth.INativeAuthPublicClientApplication
import com.microsoft.identity.nativeauth.parameters.NativeAuthSignInParameters
import com.microsoft.identity.nativeauth.statemachine.errors.MFASubmitChallengeError
import com.microsoft.identity.nativeauth.statemachine.results.GetAccessTokenResult
import com.microsoft.identity.nativeauth.statemachine.results.MFARequiredResult
Expand All @@ -36,8 +38,10 @@ import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.Ignore
import org.junit.Test
import java.util.Base64

class SignInMFATest : NativeAuthPublicClientApplicationAbstractTest() {

Expand Down Expand Up @@ -258,4 +262,72 @@ class SignInMFATest : NativeAuthPublicClientApplicationAbstractTest() {
}
}
}

/**
* Full flow:
* - SignIn specifying authentication context as claim
* - Receive MFA required error from API.
* - Request default challenge.
* - Submit correct challenge.
* - Complete MFA flow and complete sign in.
* - Check that access token contains authentication context claim.
*
*/
@Ignore("Retrieving OTP code failure and missing AC username")
@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)
resources = config.resources
val authenticationContextId = "c4"
val authenticationContextRequestClaimJson = "{\"access_token\":{\"acrs\":{\"essential\":true,\"value\":\"$authenticationContextId\"}}}"
val authenticationContextATClaimJson = "\"acrs\":[\"$authenticationContextId\"]"

retryOperation {
runBlocking {
val username = config.email
val password = getSafePassword()
val params = NativeAuthSignInParameters(username)
params.password = password.toCharArray()
params.claimsRequest = ClaimsRequest.getClaimsRequestFromJsonString(authenticationContextRequestClaimJson)

val result = application.signIn(params)
assertResult<SignInResult.MFARequired>(result)

// Initiate challenge, send code to email
val sendChallengeResult =
(result as SignInResult.MFARequired).nextState.requestChallenge()
assertResult<MFARequiredResult.VerificationRequired>(sendChallengeResult)
(sendChallengeResult as MFARequiredResult.VerificationRequired)
assertNotNull(sendChallengeResult.sentTo)
assertNotNull(sendChallengeResult.codeLength)
assertNotNull(sendChallengeResult.channel)

// Retrieve challenge from mailbox and submit
val otp = tempEmailApi.retrieveCodeFromInbox(username)
val submitCorrectChallengeResult = sendChallengeResult.nextState.submitChallenge(otp)
assertResult<SignInResult.Complete>(submitCorrectChallengeResult)

// Retrieve access token
val accountState = (submitCorrectChallengeResult as SignInResult.Complete).resultValue
val getAccessTokenResult = accountState.getAccessToken()
assertResult<GetAccessTokenResult.Complete>(getAccessTokenResult)
val authResult = (getAccessTokenResult as GetAccessTokenResult.Complete).resultValue

// Check that AT contains authentication context claim
val atParts = authResult.accessToken.split(".")
if (atParts.size != 3) {
fail("Invalid Access token received")
return@runBlocking
}
val atBody = atParts[1]
val charset = charset("UTF-8")
val atDecoded = String(
Base64.getUrlDecoder().decode(atBody.toByteArray(charset)),
charset
)
assertTrue(atDecoded.contains(authenticationContextATClaimJson))
}
}
}
}