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