Skip to content

Merge Release/2.0.0 back to dev #2618

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

Merged
merged 21 commits into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
## [TBD]
## [2.0.0]
* Use a single family refresh token (#2550)
* Removed deprecated APIs, including legacy initializers, account management methods and token acquisition methods, and the MSALTelemetry interface (#2577)
* Enforced requirement for a valid ParentViewController (with a window) in interactive token requests (#2590)
* Removed deprecated methods from native auth public interface (#2588)
* Removed the deprecated MSALLogger interface and implementation class (#2591)
* Enforced a valid broker-capable redirect URI format for AAD scenarios (#2592)
* Merged the MSALAccount (MultiTenantAccount) category into the MSALAccount protocol and removed the MSALAccount+MultiTenantAccount.h (#2594)
* Added [MSAL 2.x Migration Guide](docs/MSAL_2x_Migration_Guide.md) to assist developers in upgrading from MSAL 1.x to 2.x. (#2614)

## [1.9.0]
* Add feature flags provider to be controlled from broker (#2540)
Expand Down
2 changes: 1 addition & 1 deletion MSAL.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "MSAL"
s.version = "1.9.0"
s.version = "2.0.0"
s.summary = "Microsoft Authentication Library (MSAL) for iOS"
s.description = <<-DESC
The MSAL library for iOS gives your app the ability to begin using the Microsoft Cloud by supporting Microsoft Azure Active Directory and Microsoft Accounts in a converged experience using industry standard OAuth2 and OpenID Connect. The library also supports Microsoft Azure B2C for those using our hosted identity management service.
Expand Down
2 changes: 1 addition & 1 deletion MSAL/resources/ios/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.9.0</string>
<string>2.0.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
Expand Down
2 changes: 1 addition & 1 deletion MSAL/resources/mac/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.9.0</string>
<string>2.0.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
Expand Down
4 changes: 2 additions & 2 deletions MSAL/src/MSAL_Internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
//
//------------------------------------------------------------------------------

#define MSAL_VER_HIGH 1
#define MSAL_VER_LOW 9
#define MSAL_VER_HIGH 2
#define MSAL_VER_LOW 0
#define MSAL_VER_PATCH 0

#define STR_HELPER(x) #x
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,11 +277,19 @@ final class MSALNativeAuthJITController: MSALNativeAuthBaseController, MSALNativ
telemetryId: .telemetryApiISignInAfterJIT,
context: context)
switch response.result {
case .success(let account):
case .completed(let account):
return .init(.completed(account), correlationId: context.correlationId(), telemetryUpdate: { [weak self] result in
self?.stopTelemetryEvent(signInEvent, context: context, delegateDispatcherResult: result)
})
case .failure(let error):
case .jitAuthMethodsSelectionRequired(_, _):
return .init(.error(error: .init(type: .generalError,
message: "Unexpected result received when trying to signIn: strong authentication method registration required.",
correlationId: context.correlationId(),
errorCodes: [],
errorUri: nil),
newState: nil),
correlationId: context.correlationId())
case .error(let error):
return .init(.error(error: .init(type: .generalError,
message: error.errorDescription,
correlationId: error.correlationId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,9 @@ enum SignInVerifyCodeResult {
case completed(MSALNativeAuthUserAccountResult)
case error(error: VerifyCodeError, newState: SignInCodeRequiredState?)
}

enum SignInAfterPreviousFlowResult {
case completed(MSALNativeAuthUserAccountResult)
case jitAuthMethodsSelectionRequired(authMethods: [MSALAuthMethod], newState: RegisterStrongAuthState)
case error(error: MSALNativeAuthError)
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ final class MSALNativeAuthSignInController: MSALNativeAuthTokenController, MSALN
format: "SignIn after previous flow not available because continuationToken is nil")
let error = SignInAfterSignUpError(message: MSALNativeAuthErrorMessage.signInNotAvailable, correlationId: context.correlationId())
stopTelemetryEvent(telemetryInfo, error: error)
return .init(.failure(error), correlationId: context.correlationId())
return .init(.error(error: error), correlationId: context.correlationId())
}
let scopes = joinScopes(scopes)
guard let request = createTokenRequest(
Expand All @@ -141,7 +141,7 @@ final class MSALNativeAuthSignInController: MSALNativeAuthTokenController, MSALN
) else {
let error = SignInAfterSignUpError(correlationId: context.correlationId())
stopTelemetryEvent(telemetryInfo, error: error)
return .init(.failure(error), correlationId: context.correlationId())
return .init(.error(error: error), correlationId: context.correlationId())
}
let response = await performAndValidateTokenRequest(request, context: context)
let result = await handleTokenResponse(response,
Expand All @@ -150,56 +150,30 @@ final class MSALNativeAuthSignInController: MSALNativeAuthTokenController, MSALN
telemetryInfo: telemetryInfo)
switch result {
case .success(let accountResult):
return .init(.success(accountResult), correlationId: context.correlationId(), telemetryUpdate: { [weak self] result in
return .init(.completed(accountResult), correlationId: context.correlationId(), telemetryUpdate: { [weak self] result in
self?.stopTelemetryEvent(telemetryInfo.event, context: context, delegateDispatcherResult: result)
})
case .awaitingMFA(_):
let error = SignInAfterSignUpError(correlationId: context.correlationId())
MSALNativeAuthLogger.log(level: .error, context: context, format: "SignIn: received unexpected MFA required API result")
self.stopTelemetryEvent(telemetryInfo.event, context: context, error: error)
return .init(.failure(error), correlationId: context.correlationId())
return .init(.error(error: error), correlationId: context.correlationId())
case .jitAuthMethodsSelectionRequired(let authMethods, let jitRequiredState):
MSALNativeAuthLogger.log(level: .info, context: context, format: "JIT required after sing in after previous flow")
let jitController = createJITController()
guard let authMethod = authMethods.first else {
let error = SignInAfterSignUpError(correlationId: context.correlationId())
MSALNativeAuthLogger.log(level: .error, context: context, format: "JIT required, did not receive any default methods")
self.stopTelemetryEvent(telemetryInfo.event, context: context, error: error)
return .init(.failure(error), correlationId: context.correlationId())
}
let jitChallengeResponse = await jitController.requestJITChallenge(
continuationToken: jitRequiredState.continuationToken,
authMethod: authMethod,
verificationContact: nil,
context: context)
switch jitChallengeResponse.result {
case .completed(let accountResult):
return .init(.success(accountResult), correlationId: context.correlationId(), telemetryUpdate: { [weak self] result in
return .init(
.jitAuthMethodsSelectionRequired(authMethods: authMethods, newState: jitRequiredState),
correlationId: context.correlationId(),
telemetryUpdate: { [weak self] result in
self?.stopTelemetryEvent(telemetryInfo.event, context: context, delegateDispatcherResult: result)
})
case .verificationRequired(_, _, _, _):
let error = SignInAfterSignUpError(correlationId: context.correlationId())
MSALNativeAuthLogger.log(level: .error,
context: context,
format: "Request JIT challenge, received verification required on SignInAfterPreviousFlow")
self.stopTelemetryEvent(telemetryInfo.event, context: context, error: error)
return .init(.failure(error), correlationId: context.correlationId())
case .error(let apiError, _):
let error = SignInAfterSignUpError(correlationId: context.correlationId())
MSALNativeAuthLogger.logPII(level: .error,
context: context,
format: "Request JIT challenge, received invalid response \(MSALLogMask.maskPII(apiError.errorDescription))")
self.stopTelemetryEvent(telemetryInfo.event, context: context, error: error)
return .init(.failure(error), correlationId: context.correlationId())
}
case .error(let error):
let error = SignInAfterSignUpError(
message: error.errorDescription,
correlationId: error.correlationId,
errorCodes: error.errorCodes,
errorUri: error.errorUri
)
return .init(.failure(error), correlationId: context.correlationId())
return .init(.error(error: error), correlationId: context.correlationId())
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ protocol MSALNativeAuthSignInControlling {

typealias SignInControllerResponse = MSALNativeAuthControllerTelemetryWrapper<SignInStartResult>
typealias SignInAfterPreviousFlowControllerResponse =
MSALNativeAuthControllerTelemetryWrapper<Result<MSALNativeAuthUserAccountResult, MSALNativeAuthError>>
MSALNativeAuthControllerTelemetryWrapper<SignInAfterPreviousFlowResult>
typealias SignInSubmitCodeControllerResponse = MSALNativeAuthControllerTelemetryWrapper<SignInVerifyCodeResult>
typealias SignInSubmitPasswordControllerResponse = MSALNativeAuthControllerTelemetryWrapper<SignInPasswordRequiredResult>
typealias SignInResendCodeControllerResponse = MSALNativeAuthControllerTelemetryWrapper<SignInResendCodeResult>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,18 @@ final class SignInAfterResetPasswordDelegateDispatcher: DelegateDispatcher<SignI
await delegate.onSignInAfterResetPasswordError(error: error)
}
}

func dispatchJITRequired(authMethods: [MSALAuthMethod], newState: RegisterStrongAuthState, correlationId: UUID) async {
if let onSignInJITRequired = delegate.onSignInStrongAuthMethodRegistration {
telemetryUpdate?(.success(()))
await onSignInJITRequired(authMethods, newState)
} else {
let error = SignInAfterResetPasswordError(
message: requiredErrorMessage(for: "onSignInStrongAuthMethodRegistration"),
correlationId: correlationId
)
telemetryUpdate?(.failure(error))
await delegate.onSignInAfterResetPasswordError(error: error)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,18 @@ final class SignInAfterSignUpDelegateDispatcher: DelegateDispatcher<SignInAfterS
await delegate.onSignInAfterSignUpError(error: error)
}
}

func dispatchJITRequired(authMethods: [MSALAuthMethod], newState: RegisterStrongAuthState, correlationId: UUID) async {
if let onSignInJITRequired = delegate.onSignInStrongAuthMethodRegistration {
telemetryUpdate?(.success(()))
await onSignInJITRequired(authMethods, newState)
} else {
let error = SignInAfterSignUpError(
message: requiredErrorMessage(for: "onSignInStrongAuthMethodRegistration"),
correlationId: correlationId
)
telemetryUpdate?(.failure(error))
await delegate.onSignInAfterSignUpError(error: error)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,13 @@ import Foundation
)

switch controllerResponse.result {
case .success(let accountResult):
case .completed(let accountResult):
await delegateDispatcher.dispatchSignInCompleted(result: accountResult, correlationId: controllerResponse.correlationId)
case .failure(let error):
case .jitAuthMethodsSelectionRequired(authMethods: let authMethods, newState: let newState):
await delegateDispatcher.dispatchJITRequired(authMethods: authMethods,
newState: newState,
correlationId: controllerResponse.correlationId)
case .error(let error):
let error = SignInAfterResetPasswordError(
message: error.errorDescription,
correlationId: error.correlationId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,13 @@ import Foundation
let delegateDispatcher = SignInAfterSignUpDelegateDispatcher(delegate: delegate, telemetryUpdate: controllerResponse.telemetryUpdate)

switch controllerResponse.result {
case .success(let accountResult):
case .completed(let accountResult):
await delegateDispatcher.dispatchSignInCompleted(result: accountResult, correlationId: controllerResponse.correlationId)
case .failure(let error):
case .jitAuthMethodsSelectionRequired(authMethods: let authMethods, newState: let newState):
await delegateDispatcher.dispatchJITRequired(authMethods: authMethods,
newState: newState,
correlationId: controllerResponse.correlationId)
case .error(let error):
await delegate.onSignInAfterSignUpError(
error: SignInAfterSignUpError(message: error.errorDescription, correlationId: error.correlationId, errorCodes: error.errorCodes)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ class MSALNativeAuthJITControllerTests: MSALNativeAuthTestCase {
codeLength: 8
)
let userAccountResult = MSALNativeAuthUserAccountResultStub.result
signInControllerMock.continuationTokenResult = .init(.success(userAccountResult), correlationId: defaultUUID)
signInControllerMock.continuationTokenResult = .init(.completed(userAccountResult), correlationId: defaultUUID)
jitResponseValidatorMock.challengeValidatedResponse = .preverified(continuationToken: "continuationToken")
jitResponseValidatorMock.continueValidatedResponse = .success(continuationToken: "continuationToken 2")
let result = await sut.requestJITChallenge(continuationToken: expectedContinuationToken, authMethod: authMethod, verificationContact: verificationContact, context: expectedContext)
Expand Down Expand Up @@ -204,7 +204,7 @@ class MSALNativeAuthJITControllerTests: MSALNativeAuthTestCase {
jitResponseValidatorMock.continueValidatedResponse = .success(continuationToken: expectedContinuationToken)
let userAccountResult = MSALNativeAuthUserAccountResultStub.result

signInControllerMock.continuationTokenResult = .init(.success(userAccountResult), correlationId: defaultUUID)
signInControllerMock.continuationTokenResult = .init(.completed(userAccountResult), correlationId: defaultUUID)
jitResponseValidatorMock.continueValidatedResponse = .success(continuationToken: expectedContinuationToken)
let result = await sut.submitJITChallenge(challenge: "123456", continuationToken: expectedContinuationToken, grantType: .oobCode, context: expectedContext)
result.telemetryUpdate?(.success(()))
Expand All @@ -218,6 +218,34 @@ class MSALNativeAuthJITControllerTests: MSALNativeAuthTestCase {
}
}

func test_whenRequestJITContinueReturnsJITRequired_ErrorShouldBeReturned() async {
let expectedContinuationToken = "continuationToken"
let expectedContext = MSALNativeAuthRequestContext(correlationId: defaultUUID)
let authMethod = MSALAuthMethod(id: "1", challengeType: "oob", loginHint: "hint", channelTargetType: MSALNativeAuthChannelType(value:"email"))
let authMethods = [authMethod]
let newState = RegisterStrongAuthState(
controller: sut,
continuationToken: expectedContinuationToken,
correlationId: defaultUUID
)

jitRequestProviderMock.mockContinueRequestFunc(MSALNativeAuthHTTPRequestMock.prepareMockRequest())
jitResponseValidatorMock.continueValidatedResponse = .success(continuationToken: expectedContinuationToken)

signInControllerMock.continuationTokenResult = .init(.jitAuthMethodsSelectionRequired(authMethods: authMethods, newState: newState), correlationId: defaultUUID)
jitResponseValidatorMock.continueValidatedResponse = .success(continuationToken: expectedContinuationToken)
let result = await sut.submitJITChallenge(challenge: "123456", continuationToken: expectedContinuationToken, grantType: .oobCode, context: expectedContext)
result.telemetryUpdate?(.success(()))

checkTelemetryEventResult(id: .telemetryApiIdJITContinue, isSuccessful: true)
if case .error(let error, let newState) = result.result {
XCTAssertEqual(error.type, .generalError)
XCTAssertNil(newState)
} else {
XCTFail("Expected verificationRequired result")
}
}

func test_whenRequestJITContinueRequestFails_ErrorShouldBeReturned() async {
let expectedContinuationToken = "continuationToken"
let expectedContext = MSALNativeAuthRequestContext(correlationId: defaultUUID)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -882,8 +882,8 @@ final class MSALNativeAuthResetPasswordControllerTests: MSALNativeAuthTestCase {

let exp2 = expectation(description: "SignInAfterResetPassword expectation")
signInControllerMock.expectation = exp2
signInControllerMock.continuationTokenResult = .init(.failure(SignInAfterResetPasswordError(correlationId: correlationId)), correlationId: correlationId)
signInControllerMock.continuationTokenResult = .init(.error(error: SignInAfterResetPasswordError(correlationId: correlationId)), correlationId: correlationId)

let parameters = MSALNativeAuthSignInAfterResetPasswordParameters()
helper.signInAfterResetPasswordState?.signIn(parameters: parameters, delegate: SignInAfterResetPasswordDelegateStub())
await fulfillment(of: [exp2], timeout: 1)
Expand Down Expand Up @@ -934,7 +934,7 @@ final class MSALNativeAuthResetPasswordControllerTests: MSALNativeAuthTestCase {

let exp2 = expectation(description: "SignInAfterResetPassword expectation")
signInControllerMock.expectation = exp2
signInControllerMock.continuationTokenResult = .init(.failure(SignInAfterResetPasswordError(correlationId: correlationId)), correlationId: correlationId)
signInControllerMock.continuationTokenResult = .init(.error(error: SignInAfterResetPasswordError(correlationId: correlationId)), correlationId: correlationId)

let parameters = MSALNativeAuthSignInAfterResetPasswordParameters()
helper.signInAfterResetPasswordState?.signIn(parameters: parameters, delegate: SignInAfterResetPasswordDelegateStub())
Expand Down
Loading
Loading