Skip to content

Commit

Permalink
Native auth: Add new E2E test for authentication context MFA, Fixes A…
Browse files Browse the repository at this point in the history
…B#3117220 (#2252)

Add new E2E test to check that MFA is triggered when authentication
context claim is specified during signIn.
Test check also that returned access token contains authentication
context claim (acrs).
This test is currently disabled cause the email OTP service issue.


[AB#3117220](https://identitydivision.visualstudio.com/Engineering/_workitems/edit/3117220)
  • Loading branch information
nilo-ms authored Feb 6, 2025
1 parent 213efa4 commit 07b9873
Showing 1 changed file with 72 additions and 0 deletions.
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))
}
}
}
}

0 comments on commit 07b9873

Please sign in to comment.