Skip to content

Commit 2895d67

Browse files
authored
add new E2E tests for capabilities (#2648)
1 parent 10f8733 commit 2895d67

File tree

3 files changed

+143
-131
lines changed

3 files changed

+143
-131
lines changed

MSAL/src/native_auth/controllers/jit/MSALNativeAuthJITController.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ final class MSALNativeAuthJITController: MSALNativeAuthBaseController, MSALNativ
243243
}
244244
}
245245

246+
// swiftlint:disable:next function_body_length
246247
private func handleSubmitChallengeResponse(
247248
_ response: MSALNativeAuthJITContinueValidatedResponse,
248249
continuationToken: String,

MSAL/test/integration/native_auth/end_to_end/mfa/MSALNativeAuthSignInJITEndToEndTests.swift

Lines changed: 109 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -33,53 +33,14 @@ final class MSALNativeAuthSignInJITEndToEndTests: MSALNativeAuthEndToEndPassword
3333
#if os(macOS)
3434
throw XCTSkip("For some reason this test now requires Keychain access, reason needs to be investigated")
3535
#endif
36-
37-
// Step 1: Create User
38-
guard let application = initialisePublicClientApplication() else {
39-
XCTFail("Failed to initialize public client application")
40-
return
41-
}
42-
4336
let username = generateSignUpRandomEmail()
44-
let password = generateRandomPassword()
45-
46-
let codeRequiredExp = expectation(description: "code required")
47-
let signUpStartDelegate = SignUpPasswordStartDelegateSpy(expectation: codeRequiredExp)
48-
49-
let signUpParam = MSALNativeAuthSignUpParameters(username: username)
50-
signUpParam.password = password
51-
signUpParam.correlationId = correlationId
52-
53-
application.signUp(parameters: signUpParam, delegate: signUpStartDelegate)
54-
55-
await fulfillment(of: [codeRequiredExp])
56-
checkSignUpStartDelegate(signUpStartDelegate)
57-
58-
guard signUpStartDelegate.onSignUpCodeRequiredCalled else {
59-
XCTFail("onSignUpCodeRequired not called")
60-
return
61-
}
62-
63-
// Step 2: Get & Submit Code for Sign Up
64-
guard let code = await retrieveCodeFor(email: username) else {
65-
XCTFail("OTP code could not be retrieved")
66-
return
67-
}
68-
69-
let signUpCompleteExp = expectation(description: "sign-up complete")
70-
let signUpVerifyCodeDelegate = SignUpVerifyCodeDelegateSpy(expectation: signUpCompleteExp)
71-
72-
signUpStartDelegate.newState?.submitCode(code: code, delegate: signUpVerifyCodeDelegate)
73-
74-
await fulfillment(of: [signUpCompleteExp])
75-
76-
guard signUpVerifyCodeDelegate.onSignUpCompletedCalled,
77-
let signInAfterSignUpState = signUpVerifyCodeDelegate.signInAfterSignUpState else {
37+
// Step 1: Create User
38+
guard let signInAfterSignUpState = await signUpInternally(username: username, password: generateRandomPassword(), application: initialisePublicClientApplication()) else {
7839
XCTFail("onSignUpCompleted not called or state is nil")
7940
return
8041
}
8142

82-
// Step 3: Attempt to Sign In automtically
43+
// Step 2: Attempt to Sign In automtically
8344
let signInExpectation = expectation(description: "signing in")
8445
let signInDelegateSpy = SignInAfterSignUpDelegateSpy(expectation: signInExpectation)
8546

@@ -96,7 +57,7 @@ final class MSALNativeAuthSignInJITEndToEndTests: MSALNativeAuthEndToEndPassword
9657
return
9758
}
9859

99-
// Step 4: Add Strong Auth Method, but don't specify verification contact so it's preverified
60+
// Step 3: Add Strong Auth Method, but don't specify verification contact so it's preverified
10061
let challengeParameters = MSALNativeAuthChallengeAuthMethodParameters(authMethod: authMethod)
10162
let challengeExpectation = expectation(description: "challenging auth method")
10263
let challengeDelegateSpy = RegisterStrongAuthChallengeDelegateSpy(expectation: challengeExpectation)
@@ -117,52 +78,14 @@ final class MSALNativeAuthSignInJITEndToEndTests: MSALNativeAuthEndToEndPassword
11778
throw XCTSkip("For some reason this test now requires Keychain access, reason needs to be investigated")
11879
#endif
11980

120-
// Step 1: Create User
121-
guard let application = initialisePublicClientApplication() else {
122-
XCTFail("Failed to initialize public client application")
123-
return
124-
}
125-
12681
let username = generateSignUpRandomEmail()
127-
let password = generateRandomPassword()
128-
129-
let codeRequiredExp = expectation(description: "code required")
130-
let signUpStartDelegate = SignUpPasswordStartDelegateSpy(expectation: codeRequiredExp)
131-
132-
let signUpParam = MSALNativeAuthSignUpParameters(username: username)
133-
signUpParam.password = password
134-
signUpParam.correlationId = correlationId
135-
136-
application.signUp(parameters: signUpParam, delegate: signUpStartDelegate)
137-
138-
await fulfillment(of: [codeRequiredExp])
139-
checkSignUpStartDelegate(signUpStartDelegate)
140-
141-
guard signUpStartDelegate.onSignUpCodeRequiredCalled else {
142-
XCTFail("onSignUpCodeRequired not called")
143-
return
144-
}
145-
146-
// Step 2: Get & Submit Code for Sign Up
147-
guard let code = await retrieveCodeFor(email: username) else {
148-
XCTFail("OTP code could not be retrieved")
149-
return
150-
}
151-
152-
let signUpCompleteExp = expectation(description: "sign-up complete")
153-
let signUpVerifyCodeDelegate = SignUpVerifyCodeDelegateSpy(expectation: signUpCompleteExp)
154-
155-
signUpStartDelegate.newState?.submitCode(code: code, delegate: signUpVerifyCodeDelegate)
156-
157-
await fulfillment(of: [signUpCompleteExp])
158-
159-
guard signUpVerifyCodeDelegate.onSignUpCompletedCalled,
160-
let signInAfterSignUpState = signUpVerifyCodeDelegate.signInAfterSignUpState else {
82+
// Step 1: Create User
83+
guard let signInAfterSignUpState = await signUpInternally(username: username, password: generateRandomPassword(), application: initialisePublicClientApplication()) else {
16184
XCTFail("onSignUpCompleted not called or state is nil")
16285
return
16386
}
16487

165-
// Step 3: Attempt to Sign In automatically
88+
// Step 2: Attempt to Sign In automatically
16689
let signInExpectation = expectation(description: "signing in")
16790
let signInDelegateSpy = SignInAfterSignUpDelegateSpy(expectation: signInExpectation)
16891

@@ -179,7 +102,7 @@ final class MSALNativeAuthSignInJITEndToEndTests: MSALNativeAuthEndToEndPassword
179102
return
180103
}
181104

182-
// Step 4: Add Strong Auth Method and specify different email
105+
// Step 3: Add Strong Auth Method and specify different email
183106
let newEmail = generateSignUpRandomEmail()
184107
let challengeParameters = MSALNativeAuthChallengeAuthMethodParameters(authMethod: authMethod)
185108
challengeParameters.verificationContact = newEmail
@@ -196,13 +119,13 @@ final class MSALNativeAuthSignInJITEndToEndTests: MSALNativeAuthEndToEndPassword
196119
return
197120
}
198121

199-
// Step 5: Get Code for Register Strong Auth
122+
// Step 4: Get Code for Register Strong Auth
200123
guard let code = await retrieveCodeFor(email: newEmail) else {
201124
XCTFail("OTP code could not be retrieved")
202125
return
203126
}
204127

205-
// Step 6: Submit Code to Register Strong Auth
128+
// Step 5: Submit Code to Register Strong Auth
206129
let submitChallengeExpectation = expectation(description: "submitChallenge")
207130
let submitChallengeDelegateSpy = RegisterStrongAuthSubmitChallengeDelegateSpy(expectation: submitChallengeExpectation)
208131

@@ -214,56 +137,24 @@ final class MSALNativeAuthSignInJITEndToEndTests: MSALNativeAuthEndToEndPassword
214137
}
215138

216139
func test_createUserAndAddDifferentEmailAsStrongAuthMethod_thenSignInSuccessfully() async throws {
217-
throw XCTSkip("Retrieving OTP failure")
140+
throw XCTSkip("Capabilities feature not available in eSTS production")
218141
#if os(macOS)
219142
throw XCTSkip("For some reason this test now requires Keychain access, reason needs to be investigated")
220143
#endif
221144

222-
// Step 1: Create User
223-
guard let application = initialisePublicClientApplication() else {
224-
XCTFail("Failed to initialize public client application")
225-
return
226-
}
227-
228145
let username = generateSignUpRandomEmail()
229146
let password = generateRandomPassword()
230-
231-
let codeRequiredExp = expectation(description: "code required")
232-
let signUpStartDelegate = SignUpPasswordStartDelegateSpy(expectation: codeRequiredExp)
233-
234-
let signUpParam = MSALNativeAuthSignUpParameters(username: username)
235-
signUpParam.password = password
236-
signUpParam.correlationId = correlationId
237-
238-
application.signUp(parameters: signUpParam, delegate: signUpStartDelegate)
239-
240-
await fulfillment(of: [codeRequiredExp])
241-
checkSignUpStartDelegate(signUpStartDelegate)
242-
243-
guard signUpStartDelegate.onSignUpCodeRequiredCalled else {
244-
XCTFail("onSignUpCodeRequired not called")
245-
return
246-
}
247-
248-
// Step 2: Get & Submit Code for Sign Up
249-
guard let code = await retrieveCodeFor(email: username) else {
250-
XCTFail("OTP code could not be retrieved")
147+
guard let application = initialisePublicClientApplication() else {
148+
XCTFail("Failed to initialize public client application")
251149
return
252150
}
253-
254-
let signUpCompleteExp = expectation(description: "sign-up complete")
255-
let signUpVerifyCodeDelegate = SignUpVerifyCodeDelegateSpy(expectation: signUpCompleteExp)
256-
257-
signUpStartDelegate.newState?.submitCode(code: code, delegate: signUpVerifyCodeDelegate)
258-
259-
await fulfillment(of: [signUpCompleteExp])
260-
261-
guard signUpVerifyCodeDelegate.onSignUpCompletedCalled else {
262-
XCTFail("onSignUpCompleted not called")
151+
// Step 1: Create User
152+
guard let _ = await signUpInternally(username: username, password: password, application: application) else {
153+
XCTFail("onSignUpCompleted not called or state is nil")
263154
return
264155
}
265156

266-
// Step 3: Attempt to Sign In with new flow
157+
// Step 2: Attempt to Sign In with new flow
267158
let signInExpectation = expectation(description: "signing in")
268159
let signInDelegateSpy = SignInPasswordStartDelegateSpy(expectation: signInExpectation)
269160

@@ -282,7 +173,7 @@ final class MSALNativeAuthSignInJITEndToEndTests: MSALNativeAuthEndToEndPassword
282173
return
283174
}
284175

285-
// Step 4: Add Strong Auth Method and specify different email
176+
// Step 3: Add Strong Auth Method and specify different email
286177
let newEmail = generateSignUpRandomEmail()
287178
let challengeParameters = MSALNativeAuthChallengeAuthMethodParameters(authMethod: authMethod)
288179
challengeParameters.verificationContact = newEmail
@@ -299,13 +190,13 @@ final class MSALNativeAuthSignInJITEndToEndTests: MSALNativeAuthEndToEndPassword
299190
return
300191
}
301192

302-
// Step 5: Get Code for Register Strong Auth
193+
// Step 4: Get Code for Register Strong Auth
303194
guard let code = await retrieveCodeFor(email: newEmail) else {
304195
XCTFail("OTP code could not be retrieved")
305196
return
306197
}
307198

308-
// Step 6: Submit Code to Register Strong Auth
199+
// Step 5: Submit Code to Register Strong Auth
309200
let submitChallengeExpectation = expectation(description: "submitChallenge")
310201
let submitChallengeDelegateSpy = RegisterStrongAuthSubmitChallengeDelegateSpy(expectation: submitChallengeExpectation)
311202

@@ -315,6 +206,95 @@ final class MSALNativeAuthSignInJITEndToEndTests: MSALNativeAuthEndToEndPassword
315206

316207
checkSubmitChallengeDelegate(submitChallengeDelegateSpy, username: username)
317208
}
209+
210+
func test_createUserAndDoNotSendCapabilities_thenBrowserRequiredIsExpected() async throws {
211+
throw XCTSkip("Retrieving OTP failure")
212+
#if os(macOS)
213+
throw XCTSkip("For some reason this test now requires Keychain access, reason needs to be investigated")
214+
#endif
215+
216+
let username = generateSignUpRandomEmail()
217+
let password = generateRandomPassword()
218+
guard let application = initialisePublicClientApplication(capabilities: []) else {
219+
XCTFail("Failed to initialize public client application")
220+
return
221+
}
222+
// Step 1: Create User
223+
guard let _ = await signUpInternally(username: username, password: password, application: application) else {
224+
XCTFail("onSignUpCompleted not called or state is nil")
225+
return
226+
}
227+
228+
// Step 2: Attempt to Sign In with new flow
229+
let signInExpectation = expectation(description: "signing in")
230+
let signInDelegateSpy = SignInPasswordStartDelegateSpy(expectation: signInExpectation)
231+
232+
let signInParameters = MSALNativeAuthSignInParameters(username: username)
233+
signInParameters.password = password
234+
signInParameters.claimsRequest = MSALClaimsRequest(jsonString: "{\"access_token\":{\"acrs\":{\"essential\":true,\"value\":\"c4\"}}}", error: nil)
235+
236+
application.signIn(parameters: signInParameters, delegate: signInDelegateSpy)
237+
238+
await fulfillment(of: [signInExpectation])
239+
240+
guard signInDelegateSpy.onSignInStrongAuthMethodRegistrationCalled,
241+
let strongAuthState = signInDelegateSpy.newStateStrongAuthMethodRegistration,
242+
let authMethod = signInDelegateSpy.authMethods?.first else {
243+
XCTFail("Sign in failed or strong auth method registration not required")
244+
return
245+
}
246+
247+
// browser required is expected here
248+
XCTAssertTrue(signInDelegateSpy.onSignInPasswordErrorCalled)
249+
XCTAssertTrue(signInDelegateSpy.error?.isBrowserRequired ?? false)
250+
XCTAssertNotNil(signInDelegateSpy.error?.errorDescription)
251+
}
252+
253+
// MARK: private methods
254+
255+
private func signUpInternally(username: String, password: String, application: MSALNativeAuthPublicClientApplication?) async -> SignInAfterSignUpState? {
256+
// Step 1: Create User
257+
guard let application = application else {
258+
XCTFail("Failed to initialize public client application")
259+
return nil
260+
}
261+
262+
let codeRequiredExp = expectation(description: "code required")
263+
let signUpStartDelegate = SignUpPasswordStartDelegateSpy(expectation: codeRequiredExp)
264+
265+
let signUpParam = MSALNativeAuthSignUpParameters(username: username)
266+
signUpParam.password = password
267+
signUpParam.correlationId = correlationId
268+
269+
application.signUp(parameters: signUpParam, delegate: signUpStartDelegate)
270+
271+
await fulfillment(of: [codeRequiredExp])
272+
checkSignUpStartDelegate(signUpStartDelegate)
273+
274+
guard signUpStartDelegate.onSignUpCodeRequiredCalled else {
275+
XCTFail("onSignUpCodeRequired not called")
276+
return nil
277+
}
278+
279+
// Step 2: Get & Submit Code for Sign Up
280+
guard let code = await retrieveCodeFor(email: username) else {
281+
XCTFail("OTP code could not be retrieved")
282+
return nil
283+
}
284+
285+
let signUpCompleteExp = expectation(description: "sign-up complete")
286+
let signUpVerifyCodeDelegate = SignUpVerifyCodeDelegateSpy(expectation: signUpCompleteExp)
287+
288+
signUpStartDelegate.newState?.submitCode(code: code, delegate: signUpVerifyCodeDelegate)
289+
290+
await fulfillment(of: [signUpCompleteExp])
291+
292+
guard signUpVerifyCodeDelegate.onSignUpCompletedCalled else {
293+
XCTFail("onSignUpCompleted not called or state is nil")
294+
return nil
295+
}
296+
return signUpVerifyCodeDelegate.signInAfterSignUpState
297+
}
318298

319299
private func checkSignUpStartDelegate(_ delegate: SignUpPasswordStartDelegateSpy) {
320300
XCTAssertTrue(delegate.onSignUpCodeRequiredCalled)

MSAL/test/integration/native_auth/end_to_end/mfa/MSALNativeAuthSignInWithMFAEndToEndTests.swift

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,11 +312,42 @@ final class MSALNativeAuthSignInWithMFAEndToEndTests: MSALNativeAuthEndToEndPass
312312

313313
XCTAssertTrue(atString.contains(authenticationContextATClaimJson))
314314
}
315+
316+
func test_signInWithMFANoCapabilities_thenBrowserRequiredIsReturned() async throws {
317+
throw XCTSkip("Capabilities feature not available in eSTS production")
318+
#if os(macOS)
319+
throw XCTSkip("For some reason this test now requires Keychain access, reason needs to be investigated")
320+
#endif
321+
guard let username = retrieveUsernameForSignInUsernamePasswordAndMFA(),
322+
let password = await retrievePasswordForSignInUsername(),
323+
let awaitingMFAState = await signInUsernameAndPassword(username: username, password: password, capabilities: [])
324+
else {
325+
XCTFail("Something went wrong")
326+
return
327+
}
328+
329+
// Request to send challenge to the default strong auth method
330+
let mfaExpectation = expectation(description: "mfa")
331+
let mfaDelegateSpy = MFARequestChallengeDelegateSpy(expectation: mfaExpectation)
332+
333+
awaitingMFAState.requestChallenge(delegate: mfaDelegateSpy)
334+
335+
await fulfillment(of: [mfaExpectation])
336+
// browser required is expected here
337+
XCTAssertTrue(mfaDelegateSpy.onMFARequestChallengeError)
338+
XCTAssertNil(mfaDelegateSpy.newStateMFARequired)
339+
XCTAssertTrue(mfaDelegateSpy.error?.isBrowserRequired ?? false)
340+
XCTAssertNotNil(mfaDelegateSpy.error?.errorDescription)
341+
}
315342

316343
// MARK: private methods
317344

318-
private func signInUsernameAndPassword(username: String, password: String) async -> AwaitingMFAState? {
319-
guard let application = initialisePublicClientApplication()
345+
private func signInUsernameAndPassword(
346+
username: String,
347+
password: String,
348+
capabilities: MSALNativeAuthCapabilities = [.mfaRequired, .registrationRequired]
349+
) async -> AwaitingMFAState? {
350+
guard let application = initialisePublicClientApplication(capabilities: capabilities)
320351
else {
321352
XCTFail("Missing information")
322353
return nil

0 commit comments

Comments
 (0)