Skip to content

Commit 04cab7c

Browse files
authored
Merge pull request #2422 from AzureAD/yuki/sign-up-e2e
IOS Sign Up E2E Test
2 parents 4e9c790 + 6704e4c commit 04cab7c

File tree

6 files changed

+605
-5
lines changed

6 files changed

+605
-5
lines changed

MSAL/MSAL.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@
9595
0D96DB3C27850F0F00DEAF87 /* MSALWipeCacheForAllAccountsConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D96DB2E27850E1300DEAF87 /* MSALWipeCacheForAllAccountsConfig.h */; settings = {ATTRIBUTES = (Public, ); }; };
9696
0D96DB3D27850F1100DEAF87 /* MSALWipeCacheForAllAccountsConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D96DB2E27850E1300DEAF87 /* MSALWipeCacheForAllAccountsConfig.h */; settings = {ATTRIBUTES = (Public, ); }; };
9797
0D96DB3E27850F1200DEAF87 /* MSALWipeCacheForAllAccountsConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D96DB2E27850E1300DEAF87 /* MSALWipeCacheForAllAccountsConfig.h */; settings = {ATTRIBUTES = (Public, ); }; };
98+
12E2160B2D11D3920000F44C /* AuthorityURLFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E2160A2D11D3920000F44C /* AuthorityURLFormat.swift */; };
99+
12E2160C2D11D3920000F44C /* AuthorityURLFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E2160A2D11D3920000F44C /* AuthorityURLFormat.swift */; };
98100
1E04572324BD5A7D00444756 /* MSALCacheItemDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E04572024BD5A7D00444756 /* MSALCacheItemDetailViewController.m */; };
99101
1E06CD6524D116F800E3D0E5 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D6A206371FC510B500755A51 /* Security.framework */; };
100102
1E1A2E042256D12F001009ED /* MSALTestAppSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = D61A64B01E5AAC5C0086D120 /* MSALTestAppSettings.m */; };
@@ -1922,6 +1924,7 @@
19221924
04D32CCF1FD8AFF3000B123E /* MSALErrorConverterTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MSALErrorConverterTests.m; sourceTree = "<group>"; };
19231925
0D96DB2E27850E1300DEAF87 /* MSALWipeCacheForAllAccountsConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MSALWipeCacheForAllAccountsConfig.h; sourceTree = "<group>"; };
19241926
0D96DB3627850E3900DEAF87 /* MSALWipeCacheForAllAccountsConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MSALWipeCacheForAllAccountsConfig.m; sourceTree = "<group>"; };
1927+
12E2160A2D11D3920000F44C /* AuthorityURLFormat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthorityURLFormat.swift; sourceTree = "<group>"; };
19251928
1E04571F24BD5A7D00444756 /* MSALCacheItemDetailViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MSALCacheItemDetailViewController.h; sourceTree = "<group>"; };
19261929
1E04572024BD5A7D00444756 /* MSALCacheItemDetailViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MSALCacheItemDetailViewController.m; sourceTree = "<group>"; };
19271930
1E1A2E052256D194001009ED /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
@@ -3425,6 +3428,7 @@
34253428
9B235D9E2A3CFB4300657331 /* MSALNativeAuthEndToEndBaseTestCase.swift */,
34263429
2809E8342C3C37B7009F14D7 /* MSALNativeAuthEndToEndPasswordTestCase.swift */,
34273430
280095EA2C32CAFC00F1653E /* ClientIdType.swift */,
3431+
12E2160A2D11D3920000F44C /* AuthorityURLFormat.swift */,
34283432
);
34293433
path = end_to_end;
34303434
sourceTree = "<group>";
@@ -6367,6 +6371,7 @@
63676371
281A0E182C21E1FD00CB30CB /* SignInDelegateSpies.swift in Sources */,
63686372
28A277D92C22ED5E00D95E00 /* MSALNativeAuthEmailCodeRetriever.swift in Sources */,
63696373
E24CE9CC2C57F1160069E2E4 /* AttributesStub.swift in Sources */,
6374+
12E2160B2D11D3920000F44C /* AuthorityURLFormat.swift in Sources */,
63706375
281A0E1B2C21E20600CB30CB /* MSALNativeAuthEndToEndBaseTestCase.swift in Sources */,
63716376
28188F652C8F4C1100CFDD05 /* MFADelegateSpies.swift in Sources */,
63726377
281A0E192C21E20000CB30CB /* MSALNativeAuthResetPasswordEndToEndTests.swift in Sources */,
@@ -7418,6 +7423,7 @@
74187423
DE1BD1062C3C284900B0888E /* SignInDelegateSpies.swift in Sources */,
74197424
DE1BD1072C3C284C00B0888E /* MSALNativeAuthResetPasswordEndToEndTests.swift in Sources */,
74207425
DE9EB8622C5CE44B00328AA4 /* AttributesStub.swift in Sources */,
7426+
12E2160C2D11D3920000F44C /* AuthorityURLFormat.swift in Sources */,
74217427
DE1BD1012C3C283C00B0888E /* MSALNativeAuthSignUpUsernameEndToEndTests.swift in Sources */,
74227428
28188F662C8F4C1100CFDD05 /* MFADelegateSpies.swift in Sources */,
74237429
DE1BD1032C3C284100B0888E /* SignUpDelegateSpies.swift in Sources */,
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//
2+
// Copyright (c) Microsoft Corporation.
3+
// All rights reserved.
4+
//
5+
// This code is licensed under the MIT License.
6+
//
7+
// Permission is hereby granted, free of charge, to any person obtaining a copy
8+
// of this software and associated documentation files(the "Software"), to deal
9+
// in the Software without restriction, including without limitation the rights
10+
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
11+
// copies of the Software, and to permit persons to whom the Software is
12+
// furnished to do so, subject to the following conditions :
13+
//
14+
// The above copyright notice and this permission notice shall be included in
15+
// all copies or substantial portions of the Software.
16+
//
17+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
// THE SOFTWARE.
24+
25+
26+
import Foundation
27+
28+
enum AuthorityURLFormat {
29+
case tenantSubdomainShortVersion
30+
case tenantSubdomainLongVersion
31+
case tenantSubdomainTenantId
32+
}

MSAL/test/integration/native_auth/end_to_end/MSALNativeAuthEndToEndBaseTestCase.swift

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class MSALNativeAuthEndToEndBaseTestCase: XCTestCase {
3333
static let clientIdEmailPasswordAttributesKey = "email_password_attributes_client_id"
3434
static let clientIdEmailCodeAttributesKey = "email_code_attributes_client_id"
3535
static let tenantSubdomainKey = "tenant_subdomain"
36+
static let tenantIdKey = "tenant_id"
3637
static let signInEmailPasswordUsernameKey = "sign_in_email_password_username"
3738
static let signInEmailPasswordMFAUsernameKey = "sign_in_email_password_mfa_username"
3839
static let signInEmailPasswordMFANoDefaultAuthMethodUsernameKey = "sign_in_email_password_mfa_no_default_username"
@@ -71,14 +72,55 @@ class MSALNativeAuthEndToEndBaseTestCase: XCTestCase {
7172

7273
func initialisePublicClientApplication(
7374
clientIdType: ClientIdType = .password,
74-
challengeTypes: MSALNativeAuthChallengeTypes = [.OOB, .password]
75+
challengeTypes: MSALNativeAuthChallengeTypes = [.OOB, .password],
76+
customAuthorityURLFormat: AuthorityURLFormat? = nil
7577
) -> MSALNativeAuthPublicClientApplication? {
7678
let clientIdKey = getClientIdKey(type: clientIdType)
77-
guard let clientId = MSALNativeAuthEndToEndBaseTestCase.nativeAuthConfFileContent?[clientIdKey] as? String, let tenantSubdomain = MSALNativeAuthEndToEndBaseTestCase.nativeAuthConfFileContent?[Constants.tenantSubdomainKey] as? String else {
78-
XCTFail("ClientId or tenantSubdomain not found in conf.json")
79+
guard let clientId = MSALNativeAuthEndToEndBaseTestCase.nativeAuthConfFileContent?[clientIdKey] as? String else {
80+
XCTFail("ClientId not found in conf.json")
7981
return nil
8082
}
81-
return try? MSALNativeAuthPublicClientApplication(clientId: clientId, tenantSubdomain: tenantSubdomain, challengeTypes: challengeTypes)
83+
84+
guard let tenantSubdomain = MSALNativeAuthEndToEndBaseTestCase.nativeAuthConfFileContent?[Constants.tenantSubdomainKey] as? String else {
85+
XCTFail("TenantSubdomain not found in conf.json")
86+
return nil
87+
}
88+
89+
guard let tenantId = MSALNativeAuthEndToEndBaseTestCase.nativeAuthConfFileContent?[Constants.tenantIdKey] as? String else {
90+
XCTFail("TenantId not found in conf.json")
91+
return nil
92+
}
93+
94+
95+
if let customAuthorityURLFormat = customAuthorityURLFormat {
96+
let customSubdomain = getAuthorityURLString(
97+
tenantSubdomain: tenantSubdomain,
98+
tenantId: tenantId,
99+
format: customAuthorityURLFormat
100+
)
101+
102+
let authority = try? MSALCIAMAuthority(
103+
url: URL(string: customSubdomain)!,
104+
validateFormat: false
105+
)
106+
107+
let configuration = MSALPublicClientApplicationConfig(
108+
clientId: clientId,
109+
redirectUri: nil,
110+
authority: authority
111+
)
112+
113+
return try? MSALNativeAuthPublicClientApplication(
114+
configuration: configuration,
115+
challengeTypes: challengeTypes
116+
)
117+
} else {
118+
return try? MSALNativeAuthPublicClientApplication(
119+
clientId: clientId,
120+
tenantSubdomain: tenantSubdomain,
121+
challengeTypes: challengeTypes
122+
)
123+
}
82124
}
83125

84126
func generateSignUpRandomEmail() -> String {
@@ -129,4 +171,15 @@ class MSALNativeAuthEndToEndBaseTestCase: XCTestCase {
129171
return Constants.clientIdEmailCodeAttributesKey
130172
}
131173
}
174+
175+
private func getAuthorityURLString(tenantSubdomain: String, tenantId: String, format: AuthorityURLFormat) -> String {
176+
switch format {
177+
case .tenantSubdomainShortVersion:
178+
return String(format: "https://%@.ciamlogin.com/", tenantSubdomain)
179+
case .tenantSubdomainLongVersion:
180+
return String(format: "https://%@.ciamlogin.com/%@.onmicrosoft.com", tenantSubdomain, tenantSubdomain)
181+
case .tenantSubdomainTenantId:
182+
return String(format: "https://%@.ciamlogin.com/%@", tenantSubdomain, tenantId)
183+
}
184+
}
132185
}

MSAL/test/integration/native_auth/end_to_end/sign_in/MSALNativeAuthSignInUsernameEndToEndTests.swift

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,130 @@ final class MSALNativeAuthSignInUsernameEndToEndTests: MSALNativeAuthEndToEndBas
125125
XCTAssertNotNil(signInVerifyCodeDelegateSpy.result?.idToken)
126126
XCTAssertEqual(signInVerifyCodeDelegateSpy.result?.account.username, username)
127127
}
128+
129+
// Sign In - Verify Custom URL Domain - "https://<tenantName>.ciamlogin.com/<tenantName>.onmicrosoft.com"
130+
func test_signInCustomSubdomainLongInSuccess() async throws {
131+
guard let sut = initialisePublicClientApplication(clientIdType: .code, customAuthorityURLFormat: .tenantSubdomainLongVersion), let username = retrieveUsernameForSignInCode() else {
132+
XCTFail("Missing information")
133+
return
134+
}
135+
136+
let signInExpectation = expectation(description: "signing in")
137+
let signInDelegateSpy = SignInStartDelegateSpy(expectation: signInExpectation)
138+
139+
sut.signIn(username: username, correlationId: correlationId, delegate: signInDelegateSpy)
140+
141+
await fulfillment(of: [signInExpectation])
142+
143+
guard signInDelegateSpy.onSignInCodeRequiredCalled else {
144+
XCTFail("onSignInCodeRequired not called")
145+
return
146+
}
147+
148+
XCTAssertNotNil(signInDelegateSpy.newStateCodeRequired)
149+
XCTAssertNotNil(signInDelegateSpy.sentTo)
150+
151+
// Now submit the code..
152+
153+
guard let code = await retrieveCodeFor(email: username) else {
154+
XCTFail("OTP code could not be retrieved")
155+
return
156+
}
157+
158+
let verifyCodeExpectation = expectation(description: "verifying code")
159+
let signInVerifyCodeDelegateSpy = SignInVerifyCodeDelegateSpy(expectation: verifyCodeExpectation)
160+
161+
signInDelegateSpy.newStateCodeRequired?.submitCode(code: code, delegate: signInVerifyCodeDelegateSpy)
162+
163+
await fulfillment(of: [verifyCodeExpectation])
164+
165+
XCTAssertTrue(signInVerifyCodeDelegateSpy.onSignInCompletedCalled)
166+
XCTAssertNotNil(signInVerifyCodeDelegateSpy.result)
167+
XCTAssertNotNil(signInVerifyCodeDelegateSpy.result?.idToken)
168+
XCTAssertEqual(signInVerifyCodeDelegateSpy.result?.account.username, username)
169+
}
170+
171+
// Sign In - Verify Custom URL Domain - "https://<tenantName>.ciamlogin.com/<tenantId>"
172+
func test_signInCustomSubdomainIdInSuccess() async throws {
173+
guard let sut = initialisePublicClientApplication(clientIdType: .code, customAuthorityURLFormat: .tenantSubdomainTenantId), let username = retrieveUsernameForSignInCode() else {
174+
XCTFail("Missing information")
175+
return
176+
}
177+
178+
let signInExpectation = expectation(description: "signing in")
179+
let signInDelegateSpy = SignInStartDelegateSpy(expectation: signInExpectation)
180+
181+
sut.signIn(username: username, correlationId: correlationId, delegate: signInDelegateSpy)
182+
183+
await fulfillment(of: [signInExpectation])
184+
185+
guard signInDelegateSpy.onSignInCodeRequiredCalled else {
186+
XCTFail("onSignInCodeRequired not called")
187+
return
188+
}
189+
190+
XCTAssertNotNil(signInDelegateSpy.newStateCodeRequired)
191+
XCTAssertNotNil(signInDelegateSpy.sentTo)
192+
193+
// Now submit the code..
194+
195+
guard let code = await retrieveCodeFor(email: username) else {
196+
XCTFail("OTP code could not be retrieved")
197+
return
198+
}
199+
200+
let verifyCodeExpectation = expectation(description: "verifying code")
201+
let signInVerifyCodeDelegateSpy = SignInVerifyCodeDelegateSpy(expectation: verifyCodeExpectation)
202+
203+
signInDelegateSpy.newStateCodeRequired?.submitCode(code: code, delegate: signInVerifyCodeDelegateSpy)
204+
205+
await fulfillment(of: [verifyCodeExpectation])
206+
207+
XCTAssertTrue(signInVerifyCodeDelegateSpy.onSignInCompletedCalled)
208+
XCTAssertNotNil(signInVerifyCodeDelegateSpy.result)
209+
XCTAssertNotNil(signInVerifyCodeDelegateSpy.result?.idToken)
210+
XCTAssertEqual(signInVerifyCodeDelegateSpy.result?.account.username, username)
211+
}
212+
213+
// Sign In - Verify Custom URL Domain - "https://<tenantName>.ciamlogin.com/"
214+
func test_signInCustomSubdomainShortInSuccess() async throws {
215+
guard let sut = initialisePublicClientApplication(clientIdType: .code, customAuthorityURLFormat: .tenantSubdomainShortVersion), let username = retrieveUsernameForSignInCode() else {
216+
XCTFail("Missing information")
217+
return
218+
}
219+
220+
let signInExpectation = expectation(description: "signing in")
221+
let signInDelegateSpy = SignInStartDelegateSpy(expectation: signInExpectation)
222+
223+
sut.signIn(username: username, correlationId: correlationId, delegate: signInDelegateSpy)
224+
225+
await fulfillment(of: [signInExpectation])
226+
227+
guard signInDelegateSpy.onSignInCodeRequiredCalled else {
228+
XCTFail("onSignInCodeRequired not called")
229+
return
230+
}
231+
232+
XCTAssertNotNil(signInDelegateSpy.newStateCodeRequired)
233+
XCTAssertNotNil(signInDelegateSpy.sentTo)
234+
235+
// Now submit the code..
236+
237+
guard let code = await retrieveCodeFor(email: username) else {
238+
XCTFail("OTP code could not be retrieved")
239+
return
240+
}
241+
242+
let verifyCodeExpectation = expectation(description: "verifying code")
243+
let signInVerifyCodeDelegateSpy = SignInVerifyCodeDelegateSpy(expectation: verifyCodeExpectation)
244+
245+
signInDelegateSpy.newStateCodeRequired?.submitCode(code: code, delegate: signInVerifyCodeDelegateSpy)
246+
247+
await fulfillment(of: [verifyCodeExpectation])
248+
249+
XCTAssertTrue(signInVerifyCodeDelegateSpy.onSignInCompletedCalled)
250+
XCTAssertNotNil(signInVerifyCodeDelegateSpy.result)
251+
XCTAssertNotNil(signInVerifyCodeDelegateSpy.result?.idToken)
252+
XCTAssertEqual(signInVerifyCodeDelegateSpy.result?.account.username, username)
253+
}
128254
}

0 commit comments

Comments
 (0)