@@ -31,7 +31,7 @@ final class MSALNativeAuthResetPasswordEndToEndTests: MSALNativeAuthEndToEndBase
31
31
private let codeRetryCount = 3
32
32
33
33
func test_resetPassword_withoutAutomaticSignIn_succeeds( ) async throws {
34
- throw XCTSkip ( " 1secmail service is down. Ignoring test for now. " )
34
+ throw XCTSkip ( " Retrieving OTP failure " )
35
35
36
36
guard let sut = initialisePublicClientApplication ( ) ,
37
37
let username = retrieveUsernameForResetPassword ( )
@@ -72,10 +72,207 @@ final class MSALNativeAuthResetPasswordEndToEndTests: MSALNativeAuthEndToEndBase
72
72
await fulfillment ( of: [ resetPasswordCompletedExp] )
73
73
XCTAssertTrue ( resetPasswordRequiredDelegate. onResetPasswordCompletedCalled)
74
74
}
75
+
76
+ // User Case 3.1.3. SSPR – New password being set doesn’t meet password complexity requirements set on portal
77
+ func test_resetPassword_passwordComplexity_error( ) async throws {
78
+ throw XCTSkip ( " Retrieving OTP failure " )
79
+
80
+ guard let sut = initialisePublicClientApplication ( ) ,
81
+ let username = retrieveUsernameForResetPassword ( )
82
+ else {
83
+ XCTFail ( " Missing information " )
84
+ return
85
+ }
86
+ let codeRequiredExp = expectation ( description: " code required " )
87
+ let resetPasswordStartDelegate = ResetPasswordStartDelegateSpy ( expectation: codeRequiredExp)
88
+
89
+ sut. resetPassword ( username: username, delegate: resetPasswordStartDelegate)
90
+
91
+ await fulfillment ( of: [ codeRequiredExp] )
92
+ XCTAssertTrue ( resetPasswordStartDelegate. onResetPasswordCodeRequiredCalled)
93
+
94
+ guard resetPasswordStartDelegate. onResetPasswordCodeRequiredCalled else {
95
+ XCTFail ( " onResetPasswordCodeRequired not called " )
96
+ return
97
+ }
98
+
99
+ XCTAssertEqual ( resetPasswordStartDelegate. channelTargetType? . isEmailType, true )
100
+ XCTAssertFalse ( resetPasswordStartDelegate. sentTo? . isEmpty ?? true )
101
+ XCTAssertNotNil ( resetPasswordStartDelegate. codeLength)
102
+
103
+ // Now submit the code...
104
+ let newPasswordRequiredState = await retrieveAndSubmitCode ( resetPasswordStartDelegate: resetPasswordStartDelegate,
105
+ username: username,
106
+ retries: codeRetryCount)
107
+
108
+ // Now submit the password...
109
+ let resetPasswordCompletedExp = expectation ( description: " reset password completed " )
110
+ let resetPasswordRequiredDelegate = ResetPasswordRequiredDelegateSpy ( expectation: resetPasswordCompletedExp)
111
+
112
+ let uniquePassword = " INVALID_PASSWORD "
113
+ newPasswordRequiredState? . submitPassword ( password: uniquePassword, delegate: resetPasswordRequiredDelegate)
114
+
115
+ await fulfillment ( of: [ resetPasswordCompletedExp] )
116
+ XCTAssertTrue ( resetPasswordRequiredDelegate. onResetPasswordRequiredErrorCalled)
117
+ XCTAssertEqual ( resetPasswordRequiredDelegate. error? . isInvalidPassword, true )
118
+ }
119
+
120
+ // User Case 3.1.4 SSPR - Resend email OTP
121
+ func test_resetPassword_resendCode_succeeds( ) async throws {
122
+ throw XCTSkip ( " Retrieving OTP failure " )
123
+
124
+ guard let sut = initialisePublicClientApplication ( ) ,
125
+ let username = retrieveUsernameForResetPassword ( )
126
+ else {
127
+ XCTFail ( " Missing information " )
128
+ return
129
+ }
130
+ let codeRequiredExp = expectation ( description: " code required " )
131
+ let resetPasswordStartDelegate = ResetPasswordStartDelegateSpy ( expectation: codeRequiredExp)
132
+
133
+ sut. resetPassword ( username: username, delegate: resetPasswordStartDelegate)
134
+
135
+ await fulfillment ( of: [ codeRequiredExp] )
136
+ XCTAssertTrue ( resetPasswordStartDelegate. onResetPasswordCodeRequiredCalled)
137
+
138
+ guard resetPasswordStartDelegate. onResetPasswordCodeRequiredCalled else {
139
+ XCTFail ( " onResetPasswordCodeRequired not called " )
140
+ return
141
+ }
142
+
143
+ // Now get code1...
144
+ guard let code1 = await retrieveCodeFor ( email: username) else {
145
+ XCTFail ( " OTP code could not be retrieved " )
146
+ return
147
+ }
148
+
149
+ // Resend code
150
+ let resendCodeRequiredExp = expectation ( description: " code required again " )
151
+ let resetPasswordResendCodeDelegate = ResetPasswordResendCodeDelegateSpy ( expectation: resendCodeRequiredExp)
152
+
153
+ // Call resend code method
154
+ resetPasswordStartDelegate. newState? . resendCode ( delegate: resetPasswordResendCodeDelegate)
155
+
156
+ await fulfillment ( of: [ resendCodeRequiredExp] )
157
+
158
+ // Verify that resend code method was called
159
+ XCTAssertTrue ( resetPasswordResendCodeDelegate. onResetPasswordResendCodeCodeRequiredCalled,
160
+ " Resend code method should have been called " )
161
+
162
+ // Now get code2...
163
+ guard let code2 = await retrieveCodeFor ( email: username) else {
164
+ XCTFail ( " OTP code could not be retrieved " )
165
+ return
166
+ }
167
+
168
+ // Verify that the codes are different
169
+ XCTAssertNotEqual ( code1, code2, " Resent code should be different from the original code " )
170
+
171
+ // Now submit the code...
172
+ let newPasswordRequiredState = await retrieveAndSubmitCode ( resetPasswordStartDelegate: resetPasswordStartDelegate,
173
+ username: username,
174
+ retries: codeRetryCount)
175
+
176
+ // Now submit the password...
177
+ let resetPasswordCompletedExp = expectation ( description: " reset password completed " )
178
+ let resetPasswordRequiredDelegate = ResetPasswordRequiredDelegateSpy ( expectation: resetPasswordCompletedExp)
179
+
180
+ let uniquePassword = generateRandomPassword ( )
181
+ newPasswordRequiredState? . submitPassword ( password: uniquePassword, delegate: resetPasswordRequiredDelegate)
182
+
183
+ await fulfillment ( of: [ resetPasswordCompletedExp] )
184
+ XCTAssertTrue ( resetPasswordRequiredDelegate. onResetPasswordCompletedCalled)
185
+ }
186
+
187
+ // User Case 3.1.5 SSPR - Email is not found in records
188
+ func test_resetPassword_emailNotFound_error( ) async throws {
189
+ guard let sut = initialisePublicClientApplication ( ) else {
190
+ XCTFail ( " Missing information " )
191
+ return
192
+ }
193
+
194
+ let resetPasswordFailureExp = expectation ( description: " reset password user not found " )
195
+ let resetPasswordStartDelegate = ResetPasswordStartDelegateSpy ( expectation: resetPasswordFailureExp)
196
+
197
+ let unknownUsername = UUID ( ) . uuidString + " @contoso.com "
198
+
199
+ sut. resetPassword ( username: unknownUsername, delegate: resetPasswordStartDelegate)
200
+
201
+ await fulfillment ( of: [ resetPasswordFailureExp] )
202
+
203
+ // Verify error condition
204
+ XCTAssertTrue ( resetPasswordStartDelegate. onResetPasswordErrorCalled)
205
+ XCTAssertEqual ( resetPasswordStartDelegate. error? . isUserNotFound, true )
206
+ }
207
+
208
+ // User Case 3.1.6 SSPR - When SSPR requires a challenge type not supported by the client, redirect to web-fallback
209
+ func test_resetPassword_webfallback_error( ) async throws {
210
+ guard let sut = initialisePublicClientApplication ( challengeTypes: [ . password] ) ,
211
+ let username = retrieveUsernameForResetPassword ( )
212
+ else {
213
+ XCTFail ( " Missing information " )
214
+ return
215
+ }
216
+
217
+ let resetPasswordFailureExp = expectation ( description: " reset password web-fallback " )
218
+ let resetPasswordStartDelegate = ResetPasswordStartDelegateSpy ( expectation: resetPasswordFailureExp)
219
+
220
+ sut. resetPassword ( username: username, delegate: resetPasswordStartDelegate)
221
+
222
+ await fulfillment ( of: [ resetPasswordFailureExp] )
223
+
224
+ // Verify error condition
225
+ XCTAssertTrue ( resetPasswordStartDelegate. onResetPasswordErrorCalled)
226
+ XCTAssertEqual ( resetPasswordStartDelegate. error? . isBrowserRequired, true )
227
+ }
228
+
229
+ // User Case 3.1.8 SSPR – Email exists but not linked to any password
230
+ func test_resetPassword_accoutWithoutPassword_error( ) async throws {
231
+ guard let sut = initialisePublicClientApplication ( ) ,
232
+ let username = retrieveUsernameForSignInCode ( )
233
+ else {
234
+ XCTFail ( " Missing information " )
235
+ return
236
+ }
237
+
238
+ let resetPasswordFailureExp = expectation ( description: " does not support password " )
239
+ let resetPasswordStartDelegate = ResetPasswordStartDelegateSpy ( expectation: resetPasswordFailureExp)
240
+
241
+ sut. resetPassword ( username: username, delegate: resetPasswordStartDelegate)
242
+
243
+ await fulfillment ( of: [ resetPasswordFailureExp] )
244
+
245
+ // Verify error condition
246
+ XCTAssertTrue ( resetPasswordStartDelegate. onResetPasswordErrorCalled)
247
+ XCTAssertTrue ( resetPasswordStartDelegate. error? . errorDescription? . contains ( " The tenant or user does not support native credential recovery. " ) ?? false )
248
+ }
249
+
250
+ // User Case 3.1.9 - Email exists but signup method was OTP, social, etc.
251
+ func test_resetPassword_socialAccount_error( ) async throws {
252
+ throw XCTSkip ( " Skipping test as it requires a Social account, not present in MSIDLAB " )
253
+
254
+ guard let sut = initialisePublicClientApplication ( ) else {
255
+ XCTFail ( " Missing information " )
256
+ return
257
+ }
258
+
259
+ let username = " invalid " // TODO: use social account instead
260
+
261
+ let resetPasswordFailureExp = expectation ( description: " reset password user not found " )
262
+ let resetPasswordStartDelegate = ResetPasswordStartDelegateSpy ( expectation: resetPasswordFailureExp)
263
+
264
+ sut. resetPassword ( username: username, delegate: resetPasswordStartDelegate)
265
+
266
+ await fulfillment ( of: [ resetPasswordFailureExp] )
267
+
268
+ // Verify error condition
269
+ XCTAssertTrue ( resetPasswordStartDelegate. onResetPasswordErrorCalled)
270
+ XCTAssertEqual ( resetPasswordStartDelegate. error? . isInvalidUsername, true )
271
+ }
75
272
76
273
// SSPR - with automatic sign in
77
274
func test_resetPassword_withAutomaticSignIn_succeeds( ) async throws {
78
- throw XCTSkip ( " 1secmail service is down. Ignoring test for now. " )
275
+ throw XCTSkip ( " Retrieving OTP failure " )
79
276
80
277
guard let sut = initialisePublicClientApplication ( ) ,
81
278
let username = retrieveUsernameForResetPassword ( )
0 commit comments