Skip to content
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