Skip to content

Commit

Permalink
Merge pull request #2422 from AzureAD/yuki/sign-up-e2e
Browse files Browse the repository at this point in the history
IOS Sign Up E2E Test
  • Loading branch information
Yuki-YuXin authored Jan 3, 2025
2 parents 4e9c790 + 6704e4c commit 04cab7c
Show file tree
Hide file tree
Showing 6 changed files with 605 additions and 5 deletions.
6 changes: 6 additions & 0 deletions MSAL/MSAL.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@
0D96DB3C27850F0F00DEAF87 /* MSALWipeCacheForAllAccountsConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D96DB2E27850E1300DEAF87 /* MSALWipeCacheForAllAccountsConfig.h */; settings = {ATTRIBUTES = (Public, ); }; };
0D96DB3D27850F1100DEAF87 /* MSALWipeCacheForAllAccountsConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D96DB2E27850E1300DEAF87 /* MSALWipeCacheForAllAccountsConfig.h */; settings = {ATTRIBUTES = (Public, ); }; };
0D96DB3E27850F1200DEAF87 /* MSALWipeCacheForAllAccountsConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D96DB2E27850E1300DEAF87 /* MSALWipeCacheForAllAccountsConfig.h */; settings = {ATTRIBUTES = (Public, ); }; };
12E2160B2D11D3920000F44C /* AuthorityURLFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E2160A2D11D3920000F44C /* AuthorityURLFormat.swift */; };
12E2160C2D11D3920000F44C /* AuthorityURLFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E2160A2D11D3920000F44C /* AuthorityURLFormat.swift */; };
1E04572324BD5A7D00444756 /* MSALCacheItemDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E04572024BD5A7D00444756 /* MSALCacheItemDetailViewController.m */; };
1E06CD6524D116F800E3D0E5 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D6A206371FC510B500755A51 /* Security.framework */; };
1E1A2E042256D12F001009ED /* MSALTestAppSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = D61A64B01E5AAC5C0086D120 /* MSALTestAppSettings.m */; };
Expand Down Expand Up @@ -1922,6 +1924,7 @@
04D32CCF1FD8AFF3000B123E /* MSALErrorConverterTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MSALErrorConverterTests.m; sourceTree = "<group>"; };
0D96DB2E27850E1300DEAF87 /* MSALWipeCacheForAllAccountsConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MSALWipeCacheForAllAccountsConfig.h; sourceTree = "<group>"; };
0D96DB3627850E3900DEAF87 /* MSALWipeCacheForAllAccountsConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MSALWipeCacheForAllAccountsConfig.m; sourceTree = "<group>"; };
12E2160A2D11D3920000F44C /* AuthorityURLFormat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthorityURLFormat.swift; sourceTree = "<group>"; };
1E04571F24BD5A7D00444756 /* MSALCacheItemDetailViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MSALCacheItemDetailViewController.h; sourceTree = "<group>"; };
1E04572024BD5A7D00444756 /* MSALCacheItemDetailViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MSALCacheItemDetailViewController.m; sourceTree = "<group>"; };
1E1A2E052256D194001009ED /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
Expand Down Expand Up @@ -3425,6 +3428,7 @@
9B235D9E2A3CFB4300657331 /* MSALNativeAuthEndToEndBaseTestCase.swift */,
2809E8342C3C37B7009F14D7 /* MSALNativeAuthEndToEndPasswordTestCase.swift */,
280095EA2C32CAFC00F1653E /* ClientIdType.swift */,
12E2160A2D11D3920000F44C /* AuthorityURLFormat.swift */,
);
path = end_to_end;
sourceTree = "<group>";
Expand Down Expand Up @@ -6367,6 +6371,7 @@
281A0E182C21E1FD00CB30CB /* SignInDelegateSpies.swift in Sources */,
28A277D92C22ED5E00D95E00 /* MSALNativeAuthEmailCodeRetriever.swift in Sources */,
E24CE9CC2C57F1160069E2E4 /* AttributesStub.swift in Sources */,
12E2160B2D11D3920000F44C /* AuthorityURLFormat.swift in Sources */,
281A0E1B2C21E20600CB30CB /* MSALNativeAuthEndToEndBaseTestCase.swift in Sources */,
28188F652C8F4C1100CFDD05 /* MFADelegateSpies.swift in Sources */,
281A0E192C21E20000CB30CB /* MSALNativeAuthResetPasswordEndToEndTests.swift in Sources */,
Expand Down Expand Up @@ -7418,6 +7423,7 @@
DE1BD1062C3C284900B0888E /* SignInDelegateSpies.swift in Sources */,
DE1BD1072C3C284C00B0888E /* MSALNativeAuthResetPasswordEndToEndTests.swift in Sources */,
DE9EB8622C5CE44B00328AA4 /* AttributesStub.swift in Sources */,
12E2160C2D11D3920000F44C /* AuthorityURLFormat.swift in Sources */,
DE1BD1012C3C283C00B0888E /* MSALNativeAuthSignUpUsernameEndToEndTests.swift in Sources */,
28188F662C8F4C1100CFDD05 /* MFADelegateSpies.swift in Sources */,
DE1BD1032C3C284100B0888E /* SignUpDelegateSpies.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// Copyright (c) Microsoft Corporation.
// All rights reserved.
//
// This code is licensed under the MIT License.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.


import Foundation

enum AuthorityURLFormat {
case tenantSubdomainShortVersion
case tenantSubdomainLongVersion
case tenantSubdomainTenantId
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class MSALNativeAuthEndToEndBaseTestCase: XCTestCase {
static let clientIdEmailPasswordAttributesKey = "email_password_attributes_client_id"
static let clientIdEmailCodeAttributesKey = "email_code_attributes_client_id"
static let tenantSubdomainKey = "tenant_subdomain"
static let tenantIdKey = "tenant_id"
static let signInEmailPasswordUsernameKey = "sign_in_email_password_username"
static let signInEmailPasswordMFAUsernameKey = "sign_in_email_password_mfa_username"
static let signInEmailPasswordMFANoDefaultAuthMethodUsernameKey = "sign_in_email_password_mfa_no_default_username"
Expand Down Expand Up @@ -71,14 +72,55 @@ class MSALNativeAuthEndToEndBaseTestCase: XCTestCase {

func initialisePublicClientApplication(
clientIdType: ClientIdType = .password,
challengeTypes: MSALNativeAuthChallengeTypes = [.OOB, .password]
challengeTypes: MSALNativeAuthChallengeTypes = [.OOB, .password],
customAuthorityURLFormat: AuthorityURLFormat? = nil
) -> MSALNativeAuthPublicClientApplication? {
let clientIdKey = getClientIdKey(type: clientIdType)
guard let clientId = MSALNativeAuthEndToEndBaseTestCase.nativeAuthConfFileContent?[clientIdKey] as? String, let tenantSubdomain = MSALNativeAuthEndToEndBaseTestCase.nativeAuthConfFileContent?[Constants.tenantSubdomainKey] as? String else {
XCTFail("ClientId or tenantSubdomain not found in conf.json")
guard let clientId = MSALNativeAuthEndToEndBaseTestCase.nativeAuthConfFileContent?[clientIdKey] as? String else {
XCTFail("ClientId not found in conf.json")
return nil
}
return try? MSALNativeAuthPublicClientApplication(clientId: clientId, tenantSubdomain: tenantSubdomain, challengeTypes: challengeTypes)

guard let tenantSubdomain = MSALNativeAuthEndToEndBaseTestCase.nativeAuthConfFileContent?[Constants.tenantSubdomainKey] as? String else {
XCTFail("TenantSubdomain not found in conf.json")
return nil
}

guard let tenantId = MSALNativeAuthEndToEndBaseTestCase.nativeAuthConfFileContent?[Constants.tenantIdKey] as? String else {
XCTFail("TenantId not found in conf.json")
return nil
}


if let customAuthorityURLFormat = customAuthorityURLFormat {
let customSubdomain = getAuthorityURLString(
tenantSubdomain: tenantSubdomain,
tenantId: tenantId,
format: customAuthorityURLFormat
)

let authority = try? MSALCIAMAuthority(
url: URL(string: customSubdomain)!,
validateFormat: false
)

let configuration = MSALPublicClientApplicationConfig(
clientId: clientId,
redirectUri: nil,
authority: authority
)

return try? MSALNativeAuthPublicClientApplication(
configuration: configuration,
challengeTypes: challengeTypes
)
} else {
return try? MSALNativeAuthPublicClientApplication(
clientId: clientId,
tenantSubdomain: tenantSubdomain,
challengeTypes: challengeTypes
)
}
}

func generateSignUpRandomEmail() -> String {
Expand Down Expand Up @@ -129,4 +171,15 @@ class MSALNativeAuthEndToEndBaseTestCase: XCTestCase {
return Constants.clientIdEmailCodeAttributesKey
}
}

private func getAuthorityURLString(tenantSubdomain: String, tenantId: String, format: AuthorityURLFormat) -> String {
switch format {
case .tenantSubdomainShortVersion:
return String(format: "https://%@.ciamlogin.com/", tenantSubdomain)
case .tenantSubdomainLongVersion:
return String(format: "https://%@.ciamlogin.com/%@.onmicrosoft.com", tenantSubdomain, tenantSubdomain)
case .tenantSubdomainTenantId:
return String(format: "https://%@.ciamlogin.com/%@", tenantSubdomain, tenantId)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,130 @@ final class MSALNativeAuthSignInUsernameEndToEndTests: MSALNativeAuthEndToEndBas
XCTAssertNotNil(signInVerifyCodeDelegateSpy.result?.idToken)
XCTAssertEqual(signInVerifyCodeDelegateSpy.result?.account.username, username)
}

// Sign In - Verify Custom URL Domain - "https://<tenantName>.ciamlogin.com/<tenantName>.onmicrosoft.com"
func test_signInCustomSubdomainLongInSuccess() async throws {
guard let sut = initialisePublicClientApplication(clientIdType: .code, customAuthorityURLFormat: .tenantSubdomainLongVersion), let username = retrieveUsernameForSignInCode() else {
XCTFail("Missing information")
return
}

let signInExpectation = expectation(description: "signing in")
let signInDelegateSpy = SignInStartDelegateSpy(expectation: signInExpectation)

sut.signIn(username: username, correlationId: correlationId, delegate: signInDelegateSpy)

await fulfillment(of: [signInExpectation])

guard signInDelegateSpy.onSignInCodeRequiredCalled else {
XCTFail("onSignInCodeRequired not called")
return
}

XCTAssertNotNil(signInDelegateSpy.newStateCodeRequired)
XCTAssertNotNil(signInDelegateSpy.sentTo)

// Now submit the code..

guard let code = await retrieveCodeFor(email: username) else {
XCTFail("OTP code could not be retrieved")
return
}

let verifyCodeExpectation = expectation(description: "verifying code")
let signInVerifyCodeDelegateSpy = SignInVerifyCodeDelegateSpy(expectation: verifyCodeExpectation)

signInDelegateSpy.newStateCodeRequired?.submitCode(code: code, delegate: signInVerifyCodeDelegateSpy)

await fulfillment(of: [verifyCodeExpectation])

XCTAssertTrue(signInVerifyCodeDelegateSpy.onSignInCompletedCalled)
XCTAssertNotNil(signInVerifyCodeDelegateSpy.result)
XCTAssertNotNil(signInVerifyCodeDelegateSpy.result?.idToken)
XCTAssertEqual(signInVerifyCodeDelegateSpy.result?.account.username, username)
}

// Sign In - Verify Custom URL Domain - "https://<tenantName>.ciamlogin.com/<tenantId>"
func test_signInCustomSubdomainIdInSuccess() async throws {
guard let sut = initialisePublicClientApplication(clientIdType: .code, customAuthorityURLFormat: .tenantSubdomainTenantId), let username = retrieveUsernameForSignInCode() else {
XCTFail("Missing information")
return
}

let signInExpectation = expectation(description: "signing in")
let signInDelegateSpy = SignInStartDelegateSpy(expectation: signInExpectation)

sut.signIn(username: username, correlationId: correlationId, delegate: signInDelegateSpy)

await fulfillment(of: [signInExpectation])

guard signInDelegateSpy.onSignInCodeRequiredCalled else {
XCTFail("onSignInCodeRequired not called")
return
}

XCTAssertNotNil(signInDelegateSpy.newStateCodeRequired)
XCTAssertNotNil(signInDelegateSpy.sentTo)

// Now submit the code..

guard let code = await retrieveCodeFor(email: username) else {
XCTFail("OTP code could not be retrieved")
return
}

let verifyCodeExpectation = expectation(description: "verifying code")
let signInVerifyCodeDelegateSpy = SignInVerifyCodeDelegateSpy(expectation: verifyCodeExpectation)

signInDelegateSpy.newStateCodeRequired?.submitCode(code: code, delegate: signInVerifyCodeDelegateSpy)

await fulfillment(of: [verifyCodeExpectation])

XCTAssertTrue(signInVerifyCodeDelegateSpy.onSignInCompletedCalled)
XCTAssertNotNil(signInVerifyCodeDelegateSpy.result)
XCTAssertNotNil(signInVerifyCodeDelegateSpy.result?.idToken)
XCTAssertEqual(signInVerifyCodeDelegateSpy.result?.account.username, username)
}

// Sign In - Verify Custom URL Domain - "https://<tenantName>.ciamlogin.com/"
func test_signInCustomSubdomainShortInSuccess() async throws {
guard let sut = initialisePublicClientApplication(clientIdType: .code, customAuthorityURLFormat: .tenantSubdomainShortVersion), let username = retrieveUsernameForSignInCode() else {
XCTFail("Missing information")
return
}

let signInExpectation = expectation(description: "signing in")
let signInDelegateSpy = SignInStartDelegateSpy(expectation: signInExpectation)

sut.signIn(username: username, correlationId: correlationId, delegate: signInDelegateSpy)

await fulfillment(of: [signInExpectation])

guard signInDelegateSpy.onSignInCodeRequiredCalled else {
XCTFail("onSignInCodeRequired not called")
return
}

XCTAssertNotNil(signInDelegateSpy.newStateCodeRequired)
XCTAssertNotNil(signInDelegateSpy.sentTo)

// Now submit the code..

guard let code = await retrieveCodeFor(email: username) else {
XCTFail("OTP code could not be retrieved")
return
}

let verifyCodeExpectation = expectation(description: "verifying code")
let signInVerifyCodeDelegateSpy = SignInVerifyCodeDelegateSpy(expectation: verifyCodeExpectation)

signInDelegateSpy.newStateCodeRequired?.submitCode(code: code, delegate: signInVerifyCodeDelegateSpy)

await fulfillment(of: [verifyCodeExpectation])

XCTAssertTrue(signInVerifyCodeDelegateSpy.onSignInCompletedCalled)
XCTAssertNotNil(signInVerifyCodeDelegateSpy.result)
XCTAssertNotNil(signInVerifyCodeDelegateSpy.result?.idToken)
XCTAssertEqual(signInVerifyCodeDelegateSpy.result?.account.username, username)
}
}
Loading

0 comments on commit 04cab7c

Please sign in to comment.