Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IOS Sign Up E2E Test #2422

Merged
merged 43 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
2565b80
1.1.10 & 1.1.12
Yuki-YuXin Dec 11, 2024
3247d2a
2.1.11
Yuki-YuXin Dec 11, 2024
fd3a63a
1.1.13
Yuki-YuXin Dec 11, 2024
a469024
1.1.2
Yuki-YuXin Dec 11, 2024
9974b64
1.1.5
Yuki-YuXin Dec 11, 2024
578c479
2.1.10
Yuki-YuXin Dec 11, 2024
24e6838
2.1.10 move to the right place
Yuki-YuXin Dec 11, 2024
2bf3f9e
2.1.5
Yuki-YuXin Dec 11, 2024
e90dadc
2.1.6
Yuki-YuXin Dec 11, 2024
94b550b
2.1.7&2.1.8
Yuki-YuXin Dec 12, 2024
db431c4
pass 2.1.11
Yuki-YuXin Dec 12, 2024
9cd2c77
1.1.10 pass
Yuki-YuXin Dec 12, 2024
b197a17
1.1.11 pass
Yuki-YuXin Dec 12, 2024
95bfcaf
1.1.12 pass
Yuki-YuXin Dec 12, 2024
b7e826d
1.1.13 pass
Yuki-YuXin Dec 12, 2024
dc67b95
1.1.2 pass
Yuki-YuXin Dec 12, 2024
91e59f0
1.1.5 pass
Yuki-YuXin Dec 12, 2024
c2ff7f7
2.1.10 pass + use correct error message
Yuki-YuXin Dec 12, 2024
273af9b
2.1.5 pass
Yuki-YuXin Dec 12, 2024
e163297
2.1.6 pass
Yuki-YuXin Dec 12, 2024
4ab385a
2.1.7 pass
Yuki-YuXin Dec 12, 2024
08ba0c8
2.1.8 pass
Yuki-YuXin Dec 12, 2024
61569bb
remove correlation id one because it's out of scope
Yuki-YuXin Dec 12, 2024
faa59c5
Verify Custom URL Domain - Sign In
Yuki-YuXin Dec 12, 2024
886d1fe
add signInCustomDomain2InSuccess and skip
Yuki-YuXin Dec 12, 2024
6a82162
tenant_id + replicate name
Yuki-YuXin Dec 13, 2024
f4b68df
trigger pipeline
Yuki-YuXin Dec 16, 2024
d1ce495
wrong key
Yuki-YuXin Dec 16, 2024
cc3864d
Merge branch 'dev' of https://github.com/AzureAD/microsoft-authentica…
Yuki-YuXin Dec 17, 2024
14531e1
Address comments
Yuki-YuXin Dec 17, 2024
4463c60
Revert "Address comments"
Yuki-YuXin Dec 17, 2024
9b56bc2
Address comments
Yuki-YuXin Dec 17, 2024
3812849
Revert "Address comments"
Yuki-YuXin Dec 18, 2024
cefac96
Revert "Revert "Address comments""
Yuki-YuXin Dec 18, 2024
99d3b11
Use OTP instead of password to customURL
Yuki-YuXin Dec 18, 2024
7fe3bc8
Address comments
Yuki-YuXin Dec 19, 2024
1422d14
Pass the local test
Yuki-YuXin Dec 19, 2024
d239b4c
Skipping failing E2E tests for macOS due to Keychain access required …
spetrescu84 Dec 20, 2024
4f65bff
Revert "Skipping failing E2E tests for macOS due to Keychain access r…
spetrescu84 Dec 20, 2024
1c93d83
Switch PR validation to proper macOS version
spetrescu84 Dec 20, 2024
96c3908
Revert "Switch PR validation to proper macOS version"
spetrescu84 Dec 20, 2024
08ef259
Merge branch 'dev' of https://github.com/AzureAD/microsoft-authentica…
Yuki-YuXin Dec 25, 2024
6704e4c
Merge branch 'dev' of https://github.com/AzureAD/microsoft-authentica…
Yuki-YuXin Jan 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pull request does not update CHANGELOG.md.

Please consider if this change would be noticeable to a partner or user and either update CHANGELOG.md or resolve this conversation.

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 = getCustomTenantSubdomain(
tenantName: 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 getCustomTenantSubdomain(tenantName: String, tenantId: String, format: AuthorityURLFormat) -> String {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please call this method getAuthotityURLString ? It seems more accurate.

Suggested change
private func getCustomTenantSubdomain(tenantName: String, tenantId: String, format: AuthorityURLFormat) -> String {
private func getAuthotityURLString(tenantSubdomain: String, tenantId: String, format: AuthorityURLFormat) -> String {

And could you please use tenantSubdomain instead of tenantName. They are two different properties

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

switch format {
case .tenantSubdomainShortVersion:
return String(format: "https://%@.ciamlogin.com/", tenantName)
case .tenantSubdomainLongVersion:
return String(format: "https://%@.ciamlogin.com/%@.onmicrosoft.com", tenantName, tenantName)
case .tenantSubdomainTenantId:
return String(format: "https://%@.ciamlogin.com/%@", tenantName, 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_signInCustomDomain1InSuccess() async throws {
Copy link
Contributor

@nilo-ms nilo-ms Dec 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please include the name of the authority URL format in use instead of a number? Same comment applies for other tests

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

guard let sut = initialisePublicClientApplication(clientIdType: .code, customAuthorityURLFormat: AuthorityURLFormat.tenantSubdomainLongVersion), let username = retrieveUsernameForSignInCode() else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: you can avoid specifying the Enum name here :)

Suggested change
guard let sut = initialisePublicClientApplication(clientIdType: .code, customAuthorityURLFormat: AuthorityURLFormat.tenantSubdomainLongVersion), let username = retrieveUsernameForSignInCode() else {
guard let sut = initialisePublicClientApplication(clientIdType: .code, customAuthorityURLFormat: .tenantSubdomainLongVersion), let username = retrieveUsernameForSignInCode() else {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

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_signInCustomDomain2InSuccess() async throws {
guard let sut = initialisePublicClientApplication(clientIdType: .code, customAuthorityURLFormat: AuthorityURLFormat.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_signInCustomDomain3InSuccess() async throws {
guard let sut = initialisePublicClientApplication(clientIdType: .code, customAuthorityURLFormat: AuthorityURLFormat.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
Loading