Skip to content

Commit

Permalink
Merge branch 'dev' into antonioalwan/12_add_automation_wpj_token_binding
Browse files Browse the repository at this point in the history
* dev: (42 commits)
  Uninstall xcpretty version 0.4.0 before installing version 0.3.0
  Move pipeline to run with Xcode 16 on MacOS 14 (#2456)
  Revert "Switch PR validation to proper macOS version"
  Switch PR validation to proper macOS version
  Revert "Skipping failing E2E tests for macOS due to Keychain access required by the Kevault component"
  Skipping failing E2E tests for macOS due to Keychain access required by the Kevault component
  Pass the local test
  Address comments
  Use OTP instead of password to customURL
  Revert "Revert "Address comments""
  Revert "Address comments"
  Address comments
  Revert "Address comments"
  Address comments
  wrong key
  trigger pipeline
  tenant_id + replicate name
  add signInCustomDomain2InSuccess and skip
  Verify Custom URL Domain - Sign In
  remove correlation id one because it's out of scope
  ...
  • Loading branch information
antonioalwan committed Jan 13, 2025
2 parents cfb4bc1 + 3170a7f commit 03f5208
Show file tree
Hide file tree
Showing 11 changed files with 642 additions and 17 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 @@ -29,6 +29,9 @@ import MSAL
final class MSALNativeAuthSignInWithMFAEndToEndTests: MSALNativeAuthEndToEndPasswordTestCase {

func test_signInUsingPasswordWithMFASubmitWrongChallengeResendChallengeThen_completeSuccessfully() async throws {
#if os(macOS)
throw XCTSkip("For some reason this test now requires Keychain access, reason needs to be investigated")
#endif
guard let username = retrieveUsernameForSignInUsernamePasswordAndMFA(),
let password = await retrievePasswordForSignInUsername(),
let awaitingMFAState = await signInUsernameAndPassword(username: username, password: password)
Expand Down Expand Up @@ -85,6 +88,9 @@ final class MSALNativeAuthSignInWithMFAEndToEndTests: MSALNativeAuthEndToEndPass
}

func test_signInUsingPasswordWithMFAGetAuthMethods_thenCompleteSuccessfully() async throws {
#if os(macOS)
throw XCTSkip("For some reason this test now requires Keychain access, reason needs to be investigated")
#endif
guard let username = retrieveUsernameForSignInUsernamePasswordAndMFA(),
let password = await retrievePasswordForSignInUsername(),
let awaitingMFAState = await signInUsernameAndPassword(username: username, password: password)
Expand Down Expand Up @@ -140,6 +146,9 @@ final class MSALNativeAuthSignInWithMFAEndToEndTests: MSALNativeAuthEndToEndPass
}

func test_signInUsingPasswordWithMFANoDefaultAuthMethod_completeSuccessfully() async throws {
#if os(macOS)
throw XCTSkip("For some reason this test now requires Keychain access, reason needs to be investigated")
#endif
guard let username = retrieveUsernameForSignInUsernamePasswordAndMFANoDefaultAuthMethod(),
let password = await retrievePasswordForSignInUsername(),
let awaitingMFAState = await signInUsernameAndPassword(username: username, password: password)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ final class MSALNativeAuthSignInUsernameAndPasswordEndToEndTests: MSALNativeAuth

// Hero Scenario 1.2.1. Sign in - Use email and password to get token
func test_signInUsingPasswordWithKnownUsernameResultsInSuccess() async throws {
#if os(macOS)
throw XCTSkip("For some reason this test now requires Keychain access, reason needs to be investigated")
#endif
guard let sut = initialisePublicClientApplication(), let username = retrieveUsernameForSignInUsernameAndPassword(), let password = await retrievePasswordForSignInUsername() else {
XCTFail("Missing information")
return
Expand Down
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 03f5208

Please sign in to comment.