diff --git a/CHANGELOG.md b/CHANGELOG.md index cd0c5c49b6..80dca51454 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## [1.3.1] +* Preferred auth method added to device information, returned from broker + ## [1.3.0] ## [1.2.22] diff --git a/MSAL.podspec b/MSAL.podspec index 94f14b6787..2fe8cb9ec1 100644 --- a/MSAL.podspec +++ b/MSAL.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "MSAL" - s.version = "1.3.0" + s.version = "1.3.1" 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. diff --git a/MSAL/IdentityCore b/MSAL/IdentityCore index a4f25cdec1..8b5a08a636 160000 --- a/MSAL/IdentityCore +++ b/MSAL/IdentityCore @@ -1 +1 @@ -Subproject commit a4f25cdec1299305f385fe36ba997ca54613a0c1 +Subproject commit 8b5a08a636490e96f3e57ce10b8e35d8a28c7aab diff --git a/MSAL/MSAL.xcodeproj/project.pbxproj b/MSAL/MSAL.xcodeproj/project.pbxproj index f13ebc146f..9166bfd047 100644 --- a/MSAL/MSAL.xcodeproj/project.pbxproj +++ b/MSAL/MSAL.xcodeproj/project.pbxproj @@ -241,6 +241,7 @@ 23FB5C1E22542B99002BF1EB /* MSALJsonDeserializable.h in Headers */ = {isa = PBXBuildFile; fileRef = 23FB5C1C22542B99002BF1EB /* MSALJsonDeserializable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2826932A2A0974750037B93A /* MSALNativeAuthTokenRequestParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 282693272A0974740037B93A /* MSALNativeAuthTokenRequestParameters.swift */; }; 2826933B2A0B98750037B93A /* MSALNativeAuthSignInParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2826933A2A0B98750037B93A /* MSALNativeAuthSignInParameters.swift */; }; + 285D0D692B99C14F002A1D4A /* MSALNativeAuthTokenResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 285D0D682B99C14F002A1D4A /* MSALNativeAuthTokenResult.swift */; }; 285F36082A24DF8300A2190F /* MSALNativeAuthSignInControlling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 285F36072A24DF8300A2190F /* MSALNativeAuthSignInControlling.swift */; }; 2877081F2A14F67400E371ED /* MSALNativeAuthSignInChallengeValidatedResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2877081E2A14F67400E371ED /* MSALNativeAuthSignInChallengeValidatedResponse.swift */; }; 287708222A151A8500E371ED /* MSALNativeAuthInternalChannelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287708212A151A8500E371ED /* MSALNativeAuthInternalChannelType.swift */; }; @@ -1540,6 +1541,7 @@ 23FB5C1C22542B99002BF1EB /* MSALJsonDeserializable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MSALJsonDeserializable.h; sourceTree = ""; }; 282693272A0974740037B93A /* MSALNativeAuthTokenRequestParameters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSALNativeAuthTokenRequestParameters.swift; sourceTree = ""; }; 2826933A2A0B98750037B93A /* MSALNativeAuthSignInParameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSALNativeAuthSignInParameters.swift; sourceTree = ""; }; + 285D0D682B99C14F002A1D4A /* MSALNativeAuthTokenResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSALNativeAuthTokenResult.swift; sourceTree = ""; }; 285F36072A24DF8300A2190F /* MSALNativeAuthSignInControlling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSALNativeAuthSignInControlling.swift; sourceTree = ""; }; 2877081E2A14F67400E371ED /* MSALNativeAuthSignInChallengeValidatedResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSALNativeAuthSignInChallengeValidatedResponse.swift; sourceTree = ""; }; 287708212A151A8500E371ED /* MSALNativeAuthInternalChannelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSALNativeAuthInternalChannelType.swift; sourceTree = ""; }; @@ -3941,6 +3943,7 @@ 9BD78D7A2A126A1500AA7E12 /* MSALNativeAuthChallengeTypes.h */, DE8BE7DB2A1F6BD2009642A5 /* MSALNativeAuthUserAccountResult.swift */, E2D3BC4E2A7BE1C4009C4D1F /* MSALNativeAuthUserAccountResult+Internal.swift */, + 285D0D682B99C14F002A1D4A /* MSALNativeAuthTokenResult.swift */, ); path = public; sourceTree = ""; @@ -5791,6 +5794,7 @@ E2F626A72A780F3D00C4A303 /* SignUpStates+Internal.swift in Sources */, 2826932A2A0974750037B93A /* MSALNativeAuthTokenRequestParameters.swift in Sources */, 28EDF93E29E6D43900A99F2A /* SignUpStartError.swift in Sources */, + 285D0D692B99C14F002A1D4A /* MSALNativeAuthTokenResult.swift in Sources */, DE94C9F029F2AF5E00C1EC1F /* MSALNativeAuthResetPasswordChallengeRequestParameters.swift in Sources */, D61BD2B31EBD09F90007E484 /* MSALPromptType.m in Sources */, DEC1E425298BE18A00948BED /* MSALNativeAuthServerTelemetry.swift in Sources */, diff --git a/MSAL/resources/ios/Info.plist b/MSAL/resources/ios/Info.plist index 18065b543d..9f9016aec0 100644 --- a/MSAL/resources/ios/Info.plist +++ b/MSAL/resources/ios/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.3.0 + 1.3.1 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/MSAL/resources/mac/Info.plist b/MSAL/resources/mac/Info.plist index 423560ac00..48e80a7a11 100644 --- a/MSAL/resources/mac/Info.plist +++ b/MSAL/resources/mac/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.3.0 + 1.3.1 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSHumanReadableCopyright diff --git a/MSAL/src/MSALDeviceInformation.m b/MSAL/src/MSALDeviceInformation.m index 371aea200c..3594fd14fe 100644 --- a/MSAL/src/MSALDeviceInformation.m +++ b/MSAL/src/MSALDeviceInformation.m @@ -87,6 +87,8 @@ - (instancetype)initWithMSIDDeviceInfo:(MSIDDeviceInfo *)deviceInfo #if TARGET_OS_OSX _platformSSOStatus = [self msalPlatformSSOStatusFromMSIDPlatformSSOStatus:deviceInfo.platformSSOStatus]; #endif + _configuredPreferredAuthMethod = [self msalPreferredAuthMethodFromMSIDPreferredAuthMethod:deviceInfo.preferredAuthConfig]; + _extraDeviceInformation = [NSMutableDictionary new]; [self initExtraDeviceInformation:deviceInfo]; } @@ -134,6 +136,17 @@ - (MSALPlatformSSOStatus)msalPlatformSSOStatusFromMSIDPlatformSSOStatus:(MSIDPla } } +- (MSALPreferredAuthMethod)msalPreferredAuthMethodFromMSIDPreferredAuthMethod:(MSIDPreferredAuthMethod)msidPreferredAuthConfig +{ + switch (msidPreferredAuthConfig) { + case MSIDPreferredAuthMethodQRPIN: + return 1; // Private enum value for QR+PIN + + default: + return MSALPreferredAuthMethodNone; + } +} + // For readability, both keys and values in the output dictionary are NSString - (void) initExtraDeviceInformation:(MSIDDeviceInfo *)deviceInfo { diff --git a/MSAL/src/MSALPublicClientApplication.m b/MSAL/src/MSALPublicClientApplication.m index 1263b11fa6..0371cf430d 100644 --- a/MSAL/src/MSALPublicClientApplication.m +++ b/MSAL/src/MSALPublicClientApplication.m @@ -1183,6 +1183,14 @@ - (void)acquireTokenWithParameters:(MSALInteractiveTokenParameters *)parameters // Extra parameters to be added to the /authorize endpoint. msidParams.extraAuthorizeURLQueryParameters = self.internalConfig.extraQueryParameters.extraAuthorizeURLQueryParameters; + // Private enum value for QR+PIN + if (parameters.preferredAuthMethod == 1) + { + NSMutableDictionary *extraAuthorizeURLQueryParameters = [msidParams.extraAuthorizeURLQueryParameters mutableCopy]; + [extraAuthorizeURLQueryParameters setObject:MSID_PREFERRED_AUTH_METHOD_QR_PIN forKey:MSID_PREFERRED_AUTH_METHOD_KEY]; + msidParams.extraAuthorizeURLQueryParameters = extraAuthorizeURLQueryParameters; + } + // Extra parameters to be added to the /token endpoint. msidParams.extraTokenRequestParameters = self.internalConfig.extraQueryParameters.extraTokenURLParameters; diff --git a/MSAL/src/MSAL_Internal.h b/MSAL/src/MSAL_Internal.h index 69cd760878..1fbddfd9eb 100644 --- a/MSAL/src/MSAL_Internal.h +++ b/MSAL/src/MSAL_Internal.h @@ -27,7 +27,7 @@ #define MSAL_VER_HIGH 1 #define MSAL_VER_LOW 3 -#define MSAL_VER_PATCH 0 +#define MSAL_VER_PATCH 1 #define STR_HELPER(x) #x #define STR(x) STR_HELPER(x) diff --git a/MSAL/src/native_auth/cache/MSALNativeAuthTokens.swift b/MSAL/src/native_auth/cache/MSALNativeAuthTokens.swift index 13105e414f..ebc8099f20 100644 --- a/MSAL/src/native_auth/cache/MSALNativeAuthTokens.swift +++ b/MSAL/src/native_auth/cache/MSALNativeAuthTokens.swift @@ -25,11 +25,11 @@ @_implementationOnly import MSAL_Private class MSALNativeAuthTokens { - let accessToken: MSIDAccessToken? + let accessToken: MSIDAccessToken let refreshToken: MSIDRefreshToken? let rawIdToken: String? - init(accessToken: MSIDAccessToken?, refreshToken: MSIDRefreshToken?, rawIdToken: String?) { + init(accessToken: MSIDAccessToken, refreshToken: MSIDRefreshToken?, rawIdToken: String?) { self.accessToken = accessToken self.refreshToken = refreshToken self.rawIdToken = rawIdToken diff --git a/MSAL/src/native_auth/controllers/credentials/MSALNativeAuthCredentialsController.swift b/MSAL/src/native_auth/controllers/credentials/MSALNativeAuthCredentialsController.swift index b882989d8b..82261faedd 100644 --- a/MSAL/src/native_auth/controllers/credentials/MSALNativeAuthCredentialsController.swift +++ b/MSAL/src/native_auth/controllers/credentials/MSALNativeAuthCredentialsController.swift @@ -86,7 +86,7 @@ final class MSALNativeAuthCredentialsController: MSALNativeAuthTokenController, func refreshToken(context: MSALNativeAuthRequestContext, authTokens: MSALNativeAuthTokens) async -> RefreshTokenCredentialControllerResponse { MSALLogger.log(level: .verbose, context: context, format: "Refresh started") let telemetryEvent = makeAndStartTelemetryEvent(id: .telemetryApiIdRefreshToken, context: context) - let scopes = authTokens.accessToken?.scopes.array as? [String] ?? [] + let scopes = authTokens.accessToken.scopes.array as? [String] ?? [] guard let request = createRefreshTokenRequest( scopes: scopes, refreshToken: authTokens.refreshToken?.refreshToken, @@ -94,7 +94,7 @@ final class MSALNativeAuthCredentialsController: MSALNativeAuthTokenController, ) else { stopTelemetryEvent(telemetryEvent, context: context, error: MSALNativeAuthInternalError.invalidRequest) return .init( - .failure(RetrieveAccessTokenError(type: .generalError, correlationId: context.correlationId())), + .failure(RetrieveAccessTokenError(type: .generalError, correlationId: context.correlationId())), correlationId: context.correlationId() ) } @@ -181,8 +181,13 @@ final class MSALNativeAuthCredentialsController: MSALNativeAuthTokenController, level: .verbose, context: context, format: "Refresh Token completed successfully") + // TODO: Handle tokenResult.refreshToken as? MSIDRefreshToken in a safer way return .init( - .success(tokenResult.accessToken.accessToken), + .success(MSALNativeAuthTokenResult(authTokens: MSALNativeAuthTokens( + accessToken: tokenResult.accessToken, + refreshToken: tokenResult.refreshToken as? MSIDRefreshToken, + rawIdToken: tokenResult.rawIdToken + ))), correlationId: context.correlationId(), telemetryUpdate: { [weak self] result in telemetryEvent?.setUserInformation(tokenResult.account) diff --git a/MSAL/src/native_auth/controllers/credentials/MSALNativeAuthCredentialsControlling.swift b/MSAL/src/native_auth/controllers/credentials/MSALNativeAuthCredentialsControlling.swift index 0e85fa4531..2b8882032e 100644 --- a/MSAL/src/native_auth/controllers/credentials/MSALNativeAuthCredentialsControlling.swift +++ b/MSAL/src/native_auth/controllers/credentials/MSALNativeAuthCredentialsControlling.swift @@ -25,7 +25,8 @@ import Foundation protocol MSALNativeAuthCredentialsControlling { - typealias RefreshTokenCredentialControllerResponse = MSALNativeAuthControllerTelemetryWrapper> + typealias RefreshTokenCredentialControllerResponse = + MSALNativeAuthControllerTelemetryWrapper> func retrieveUserAccountResult(context: MSALNativeAuthRequestContext) -> MSALNativeAuthUserAccountResult? func refreshToken(context: MSALNativeAuthRequestContext, authTokens: MSALNativeAuthTokens) async -> RefreshTokenCredentialControllerResponse diff --git a/MSAL/src/native_auth/network/errors/MSALNativeAuthErrorMessage.swift b/MSAL/src/native_auth/network/errors/MSALNativeAuthErrorMessage.swift index a814deb147..1462d82862 100644 --- a/MSAL/src/native_auth/network/errors/MSALNativeAuthErrorMessage.swift +++ b/MSAL/src/native_auth/network/errors/MSALNativeAuthErrorMessage.swift @@ -41,7 +41,6 @@ enum MSALNativeAuthErrorMessage { static let generalError = "General error" static let invalidCode = "Invalid code" static let refreshTokenExpired = "Refresh token is expired" - static let tokenNotFound = "Token not found" static let redirectUriNotSetWarning = "WARNING ⚠️: redirectUri not set during MSAL Native Auth initialization. Production apps must correctly configure a redirect URI and call acquireToken in response to all browserRequired errors. See https://learn.microsoft.com/entra/identity-platform/redirect-uris-ios" static let unexpectedResponseBody = "Unexpected response body received" static let unexpectedChallengeType = "Unexpected challenge type" diff --git a/MSAL/src/native_auth/public/MSALNativeAuthTokenResult.swift b/MSAL/src/native_auth/public/MSALNativeAuthTokenResult.swift new file mode 100644 index 0000000000..ff65e624d9 --- /dev/null +++ b/MSAL/src/native_auth/public/MSALNativeAuthTokenResult.swift @@ -0,0 +1,53 @@ +// +// 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 + +public class MSALNativeAuthTokenResult: NSObject { + + let authTokens: MSALNativeAuthTokens + + init(authTokens: MSALNativeAuthTokens) { + self.authTokens = authTokens + } + + /** + The Access Token requested. + Note that if access token is not returned in token response, this property will be returned as an empty string. + */ + @objc public var accessToken: String { + authTokens.accessToken.accessToken + } + + /// Get the list of permissions for the access token for the account. + @objc public var scopes: [String] { + authTokens.accessToken.scopes.array as? [String] ?? [] + } + + /// Get the expiration date for the access token for the account. + /// This value is calculated based on current UTC time measured locally and the value expiresIn returned from the service + @objc public var expiresOn: Date? { + authTokens.accessToken.expiresOn + } +} diff --git a/MSAL/src/native_auth/public/MSALNativeAuthUserAccountResult+Internal.swift b/MSAL/src/native_auth/public/MSALNativeAuthUserAccountResult+Internal.swift index b2afa7a7bf..12972ea676 100644 --- a/MSAL/src/native_auth/public/MSALNativeAuthUserAccountResult+Internal.swift +++ b/MSAL/src/native_auth/public/MSALNativeAuthUserAccountResult+Internal.swift @@ -34,17 +34,12 @@ extension MSALNativeAuthUserAccountResult { let context = MSALNativeAuthRequestContext(correlationId: correlationId) let correlationId = context.correlationId() - if let accessToken = self.authTokens.accessToken { - if forceRefresh || accessToken.isExpired() { - let controllerFactory = MSALNativeAuthControllerFactory(config: configuration) - let credentialsController = controllerFactory.makeCredentialsController(cacheAccessor: cacheAccessor) - return await credentialsController.refreshToken(context: context, authTokens: authTokens) - } else { - return .init(.success(accessToken.accessToken), correlationId: correlationId) - } + if forceRefresh || self.authTokens.accessToken.isExpired() { + let controllerFactory = MSALNativeAuthControllerFactory(config: configuration) + let credentialsController = controllerFactory.makeCredentialsController(cacheAccessor: cacheAccessor) + return await credentialsController.refreshToken(context: context, authTokens: authTokens) } else { - MSALLogger.log(level: .error, context: context, format: "Retrieve Access Token: Existing token not found") - return .init(.failure(RetrieveAccessTokenError(type: .tokenNotFound, correlationId: correlationId)), correlationId: correlationId) + return .init(.success(MSALNativeAuthTokenResult(authTokens: authTokens)), correlationId: correlationId) } } } diff --git a/MSAL/src/native_auth/public/MSALNativeAuthUserAccountResult.swift b/MSAL/src/native_auth/public/MSALNativeAuthUserAccountResult.swift index 90ce3efecb..960a4f5c1f 100644 --- a/MSAL/src/native_auth/public/MSALNativeAuthUserAccountResult.swift +++ b/MSAL/src/native_auth/public/MSALNativeAuthUserAccountResult.swift @@ -38,16 +38,6 @@ import Foundation authTokens.rawIdToken } - /// Get the list of permissions for the access token for the account if present. - @objc public var scopes: [String] { - authTokens.accessToken?.scopes.array as? [String] ?? [] - } - - /// Get the expiration date for the access token for the account if present. - @objc public var expiresOn: Date? { - authTokens.accessToken?.expiresOn - } - init( account: MSALAccount, authTokens: MSALNativeAuthTokens, @@ -96,9 +86,9 @@ import Foundation let delegateDispatcher = CredentialsDelegateDispatcher(delegate: delegate, telemetryUpdate: controllerResponse.telemetryUpdate) switch controllerResponse.result { - case .success(let accessToken): + case .success(let accessTokenResult): await delegateDispatcher.dispatchAccessTokenRetrieveCompleted( - accessToken: accessToken, + result: accessTokenResult, correlationId: controllerResponse.correlationId ) case .failure(let error): diff --git a/MSAL/src/native_auth/public/state_machine/delegate/CredentialsDelegates.swift b/MSAL/src/native_auth/public/state_machine/delegate/CredentialsDelegates.swift index 1d20fc6013..d946abc1b8 100644 --- a/MSAL/src/native_auth/public/state_machine/delegate/CredentialsDelegates.swift +++ b/MSAL/src/native_auth/public/state_machine/delegate/CredentialsDelegates.swift @@ -33,6 +33,6 @@ public protocol CredentialsDelegate { /// Notifies the delegate that the operation completed successfully. /// - Note: If a flow requires this optional method and it is not implemented, then ``onAccessTokenRetrieveError(error:)`` will be called. - /// - Parameter accessToken: The access token string. - @MainActor @objc optional func onAccessTokenRetrieveCompleted(accessToken: String) + /// - Parameter result: The access token result. + @MainActor @objc optional func onAccessTokenRetrieveCompleted(result: MSALNativeAuthTokenResult) } diff --git a/MSAL/src/native_auth/public/state_machine/delegate_dispatcher/CredentialsDelegateDispatcher.swift b/MSAL/src/native_auth/public/state_machine/delegate_dispatcher/CredentialsDelegateDispatcher.swift index a587579ea1..f95d6d35d1 100644 --- a/MSAL/src/native_auth/public/state_machine/delegate_dispatcher/CredentialsDelegateDispatcher.swift +++ b/MSAL/src/native_auth/public/state_machine/delegate_dispatcher/CredentialsDelegateDispatcher.swift @@ -26,10 +26,10 @@ import Foundation final class CredentialsDelegateDispatcher: DelegateDispatcher { - func dispatchAccessTokenRetrieveCompleted(accessToken: String, correlationId: UUID) async { + func dispatchAccessTokenRetrieveCompleted(result: MSALNativeAuthTokenResult, correlationId: UUID) async { if let onAccessTokenRetrieveCompleted = delegate.onAccessTokenRetrieveCompleted { telemetryUpdate?(.success(())) - await onAccessTokenRetrieveCompleted(accessToken) + await onAccessTokenRetrieveCompleted(result) } else { let error = RetrieveAccessTokenError( type: .generalError, diff --git a/MSAL/src/native_auth/public/state_machine/error/RetrieveAccessTokenError.swift b/MSAL/src/native_auth/public/state_machine/error/RetrieveAccessTokenError.swift index 4407609508..32c44f30c8 100644 --- a/MSAL/src/native_auth/public/state_machine/error/RetrieveAccessTokenError.swift +++ b/MSAL/src/native_auth/public/state_machine/error/RetrieveAccessTokenError.swift @@ -30,7 +30,6 @@ public class RetrieveAccessTokenError: MSALNativeAuthError { enum ErrorType: CaseIterable { case browserRequired case refreshTokenExpired - case tokenNotFound case generalError } @@ -52,8 +51,6 @@ public class RetrieveAccessTokenError: MSALNativeAuthError { return MSALNativeAuthErrorMessage.browserRequired case .refreshTokenExpired: return MSALNativeAuthErrorMessage.refreshTokenExpired - case .tokenNotFound: - return MSALNativeAuthErrorMessage.tokenNotFound case .generalError: return MSALNativeAuthErrorMessage.generalError } @@ -68,9 +65,4 @@ public class RetrieveAccessTokenError: MSALNativeAuthError { public var isRefreshTokenExpired: Bool { return type == .refreshTokenExpired } - - /// Returns `true` if the existing token cannot be found. - public var isTokenNotFound: Bool { - return type == .tokenNotFound - } } diff --git a/MSAL/src/native_auth/public/state_machine/state/SignUpStates.swift b/MSAL/src/native_auth/public/state_machine/state/SignUpStates.swift index b421718cb8..ff7772343a 100644 --- a/MSAL/src/native_auth/public/state_machine/state/SignUpStates.swift +++ b/MSAL/src/native_auth/public/state_machine/state/SignUpStates.swift @@ -77,7 +77,6 @@ public class SignUpBaseState: MSALNativeAuthBaseState { public func submitCode(code: String, delegate: SignUpVerifyCodeDelegate) { Task { let controllerResponse = await submitCodeInternal(code: code) - let delegateDispatcher = SignUpVerifyCodeDelegateDispatcher(delegate: delegate, telemetryUpdate: controllerResponse.telemetryUpdate) switch controllerResponse.result { diff --git a/MSAL/src/public/MSALDefinitions.h b/MSAL/src/public/MSALDefinitions.h index 7539d1eedc..3c9efc5b85 100644 --- a/MSAL/src/public/MSALDefinitions.h +++ b/MSAL/src/public/MSALDefinitions.h @@ -178,6 +178,18 @@ typedef NS_ENUM(NSUInteger, MSALPlatformSSOStatus) MSALPlatformSSOEnabledAndRegistered }; +/** + Preferred auth method for MSAL requests. Can be configured by admin as part of MSALDeviceInformation and + passed in the interactive params to the server. + */ +typedef NS_ENUM(NSUInteger, MSALPreferredAuthMethod) +{ + /* + No preferred auth method passed with the request to the authetication server. + */ + MSALPreferredAuthMethodNone +}; + /** The block that gets invoked after MSAL has finished getting a token silently or interactively. @param result Represents information returned to the application after a successful interactive or silent token acquisition. See `MSALResult` for more information. diff --git a/MSAL/src/public/MSALDeviceInformation.h b/MSAL/src/public/MSALDeviceInformation.h index 04e07e54e3..dac6a89ac4 100644 --- a/MSAL/src/public/MSALDeviceInformation.h +++ b/MSAL/src/public/MSALDeviceInformation.h @@ -69,6 +69,11 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, readonly) MSALDeviceMode deviceMode; +/** + Availability of QR+PIN as an authentication method as configured by the admin +*/ +@property (nonatomic, readonly) MSALPreferredAuthMethod configuredPreferredAuthMethod; + /** Specifies whether AAD SSO extension was detected on the device. */ diff --git a/MSAL/src/public/MSALInteractiveTokenParameters.h b/MSAL/src/public/MSALInteractiveTokenParameters.h index 32aef6f000..455dff2681 100644 --- a/MSAL/src/public/MSALInteractiveTokenParameters.h +++ b/MSAL/src/public/MSALInteractiveTokenParameters.h @@ -95,6 +95,12 @@ Modal presentation style for displaying authentication web content. */ @property (nonatomic, nullable) WKWebView *customWebview DEPRECATED_MSG_ATTRIBUTE("Create MSALWebviewParameters and provide it to -initWithScopes:webviewParameters: instead"); +/** + The pre-defined preferred auth method for the interactive request. + By default, it will be set to MSALPreferredAuthMethod.MSALPreferredAuthMethodNone. + */ +@property (nonatomic) MSALPreferredAuthMethod preferredAuthMethod; + #pragma mark - Constructing MSALInteractiveTokenParameters #if TARGET_OS_IPHONE diff --git a/MSAL/test/app/ios/resources/Info.plist b/MSAL/test/app/ios/resources/Info.plist index dc7d25d958..71949684c4 100644 --- a/MSAL/test/app/ios/resources/Info.plist +++ b/MSAL/test/app/ios/resources/Info.plist @@ -2,6 +2,8 @@ + NSCameraUsageDescription + This app may need to scan QR codes. CFBundleDevelopmentRegion en CFBundleExecutable diff --git a/MSAL/test/unit/native_auth/cache/MSALNativeAuthCacheAccessorTest.swift b/MSAL/test/unit/native_auth/cache/MSALNativeAuthCacheAccessorTest.swift index ad14e9d6ca..bc38d8ba2e 100644 --- a/MSAL/test/unit/native_auth/cache/MSALNativeAuthCacheAccessorTest.swift +++ b/MSAL/test/unit/native_auth/cache/MSALNativeAuthCacheAccessorTest.swift @@ -56,7 +56,7 @@ final class MSALNativeAuthCacheAccessorTest: XCTestCase { var tokens: MSALNativeAuthTokens? = nil XCTAssertNoThrow(tokens = try cacheAccessor.getTokens(account: parameters.account, configuration: parameters.msidConfiguration, context: contextStub)) - XCTAssertEqual(tokens?.accessToken?.accessToken, tokenResponse.accessToken) + XCTAssertEqual(tokens?.accessToken.accessToken, tokenResponse.accessToken) XCTAssertEqual(tokens?.refreshToken?.refreshToken, tokenResponse.refreshToken) XCTAssertEqual(tokens?.rawIdToken, tokenResponse.idToken) } @@ -67,7 +67,7 @@ final class MSALNativeAuthCacheAccessorTest: XCTestCase { var tokens: MSALNativeAuthTokens? = nil XCTAssertNoThrow(tokens = try cacheAccessor.getTokens(account: parameters.account, configuration: parameters.msidConfiguration, context: contextStub)) - XCTAssertEqual(tokens?.accessToken?.accessToken, tokenResponse.accessToken) + XCTAssertEqual(tokens?.accessToken.accessToken, tokenResponse.accessToken) XCTAssertEqual(tokens?.refreshToken?.refreshToken, tokenResponse.refreshToken) XCTAssertEqual(tokens?.rawIdToken, tokenResponse.idToken) @@ -80,7 +80,7 @@ final class MSALNativeAuthCacheAccessorTest: XCTestCase { XCTAssertNoThrow(try cacheAccessor.validateAndSaveTokensAndAccount(tokenResponse: tokenResponse, configuration: parameters.msidConfiguration, context: contextStub)) XCTAssertNoThrow(tokens = try cacheAccessor.getTokens(account: parameters.account, configuration: parameters.msidConfiguration, context: contextStub)) - XCTAssertEqual(tokens?.accessToken?.accessToken, newAccessToken) + XCTAssertEqual(tokens?.accessToken.accessToken, newAccessToken) XCTAssertEqual(tokens?.refreshToken?.refreshToken, newRefreshToken) XCTAssertEqual(tokens?.rawIdToken, newIdToken) } @@ -97,7 +97,7 @@ final class MSALNativeAuthCacheAccessorTest: XCTestCase { XCTAssertEqual(account?.environment, "contoso.com") XCTAssertNil(account?.accountClaims) XCTAssertNoThrow(tokens = try cacheAccessor.getTokens(account: account!, configuration: parameters.msidConfiguration, context: contextStub)) - XCTAssertEqual(tokens?.accessToken?.accessToken, tokenResponse.accessToken) + XCTAssertEqual(tokens?.accessToken.accessToken, tokenResponse.accessToken) XCTAssertEqual(tokens?.refreshToken?.refreshToken, tokenResponse.refreshToken) XCTAssertEqual(tokens?.rawIdToken, tokenResponse.idToken) } @@ -115,7 +115,7 @@ final class MSALNativeAuthCacheAccessorTest: XCTestCase { XCTAssertNil(account?.accountClaims) parameters.msidConfiguration = getMSIDConfiguration(host: "https://contoso.com/tfp/tenantName") XCTAssertNoThrow(tokens = try cacheAccessor.getTokens(account: account!, configuration: parameters.msidConfiguration, context: contextStub)) - XCTAssertEqual(tokens?.accessToken?.accessToken, tokenResponse.accessToken) + XCTAssertEqual(tokens?.accessToken.accessToken, tokenResponse.accessToken) XCTAssertEqual(tokens?.refreshToken?.refreshToken, tokenResponse.refreshToken) XCTAssertEqual(tokens?.rawIdToken, tokenResponse.idToken) } @@ -140,7 +140,7 @@ final class MSALNativeAuthCacheAccessorTest: XCTestCase { XCTAssertEqual(account?.environment, "contoso.com") XCTAssertNil(account?.accountClaims) XCTAssertNoThrow(tokens = try cacheAccessor.getTokens(account: account!, configuration: parameters.msidConfiguration, context: contextStub)) - XCTAssertEqual(tokens?.accessToken?.accessToken, newAccessToken) + XCTAssertEqual(tokens?.accessToken.accessToken, newAccessToken) XCTAssertEqual(tokens?.refreshToken?.refreshToken, newRefreshToken) XCTAssertEqual(tokens?.rawIdToken, newIdToken) } diff --git a/MSAL/test/unit/native_auth/controllers/MSALNativeAuthCredentialsControllerTests.swift b/MSAL/test/unit/native_auth/controllers/MSALNativeAuthCredentialsControllerTests.swift index 1fd52887c5..b7098bc616 100644 --- a/MSAL/test/unit/native_auth/controllers/MSALNativeAuthCredentialsControllerTests.swift +++ b/MSAL/test/unit/native_auth/controllers/MSALNativeAuthCredentialsControllerTests.swift @@ -113,8 +113,6 @@ final class MSALNativeAuthCredentialsControllerTests: MSALNativeAuthTestCase { let accountResult = sut.retrieveUserAccountResult(context: expectedContext) XCTAssertEqual(accountResult?.account.username, account.username) XCTAssertEqual(accountResult?.idToken, authTokens.rawIdToken) - XCTAssertEqual(accountResult?.scopes, authTokens.accessToken?.scopes.array as? [String]) - XCTAssertEqual(accountResult?.expiresOn, authTokens.accessToken?.expiresOn) XCTAssertTrue(NSDictionary(dictionary: accountResult?.account.accountClaims ?? [:]).isEqual(to: account.accountClaims ?? [:])) } @@ -150,7 +148,10 @@ final class MSALNativeAuthCredentialsControllerTests: MSALNativeAuthTestCase { requestProviderMock.mockRequestRefreshTokenFunc(MSALNativeAuthHTTPRequestMock.prepareMockRequest()) let expectedAccessToken = "accessToken" - let helper = CredentialsTestValidatorHelper(expectation: expectation, expectedAccessToken: expectedAccessToken) + let helper = CredentialsTestValidatorHelper(expectation: expectation, expectedResult: MSALNativeAuthTokenResult(authTokens: authTokens)) + helper.expectedAccessToken = authTokens.accessToken.accessToken + helper.expectedExpiresOn = authTokens.accessToken.expiresOn + helper.expectedScopes = authTokens.accessToken.scopes.array as? [String] ?? [] factory.mockMakeUserAccountResult(userAccountResult) tokenResult.accessToken = MSIDAccessToken() @@ -163,7 +164,7 @@ final class MSALNativeAuthCredentialsControllerTests: MSALNativeAuthTestCase { helper.onAccessTokenRetrieveCompleted(result) await fulfillment(of: [expectation], timeout: 1) - XCTAssertEqual(expectedAccessToken, authTokens.accessToken?.accessToken) + XCTAssertEqual(expectedAccessToken, authTokens.accessToken.accessToken) } func test_whenErrorIsReturnedFromValidator_itIsCorrectlyTranslatedToDelegateError() async { diff --git a/MSAL/test/unit/native_auth/controllers/factories/MSALNativeAuthResultFactoryTests.swift b/MSAL/test/unit/native_auth/controllers/factories/MSALNativeAuthResultFactoryTests.swift index 2138c7221c..1752c6d7eb 100644 --- a/MSAL/test/unit/native_auth/controllers/factories/MSALNativeAuthResultFactoryTests.swift +++ b/MSAL/test/unit/native_auth/controllers/factories/MSALNativeAuthResultFactoryTests.swift @@ -80,8 +80,6 @@ final class MSALNativeAuthResultFactoryTests: XCTestCase { } XCTAssertEqual(accountResult.account.username, username) XCTAssertEqual(accountResult.idToken, idToken) - XCTAssertEqual(accountResult.expiresOn, expiresOn) - XCTAssertEqual(accountResult.scopes, scopes) XCTAssertNotNil(accountResult.account.accountClaims) XCTAssertEqual(accountResult.account.accountClaims?.count, 21) } @@ -112,8 +110,6 @@ final class MSALNativeAuthResultFactoryTests: XCTestCase { } XCTAssertEqual(accountResult.account.username, username) XCTAssertEqual(accountResult.idToken, idToken) - XCTAssertEqual(accountResult.expiresOn, expiresOn) - XCTAssertEqual(accountResult.scopes, scopes) XCTAssertNil(accountResult.account.accountClaims) } } diff --git a/MSAL/test/unit/native_auth/mock/CredentialsDelegateSpies.swift b/MSAL/test/unit/native_auth/mock/CredentialsDelegateSpies.swift index 51568ecaf7..cf42834e27 100644 --- a/MSAL/test/unit/native_auth/mock/CredentialsDelegateSpies.swift +++ b/MSAL/test/unit/native_auth/mock/CredentialsDelegateSpies.swift @@ -29,18 +29,24 @@ open class CredentialsDelegateSpy: CredentialsDelegate { private let expectation: XCTestExpectation var expectedError: RetrieveAccessTokenError? + var expectedResult: MSALNativeAuthTokenResult? var expectedAccessToken: String? - - init(expectation: XCTestExpectation, expectedError: RetrieveAccessTokenError? = nil, expectedAccessToken: String? = nil) { + var expectedScopes: [String]? + var expectedExpiresOn: Date? + + init(expectation: XCTestExpectation, expectedError: RetrieveAccessTokenError? = nil, expectedResult: MSALNativeAuthTokenResult? = nil) { self.expectation = expectation self.expectedError = expectedError - self.expectedAccessToken = expectedAccessToken + self.expectedResult = expectedResult } - public func onAccessTokenRetrieveCompleted(accessToken: String) { - if let expectedAccessToken = expectedAccessToken { + public func onAccessTokenRetrieveCompleted(result: MSALNativeAuthTokenResult) { + if let expectedResult = expectedResult { XCTAssertTrue(Thread.isMainThread) - XCTAssertEqual(expectedAccessToken, accessToken) + XCTAssertEqual(expectedResult, expectedResult) + XCTAssertEqual(expectedResult.accessToken, expectedAccessToken) + XCTAssertEqual(expectedResult.scopes, expectedScopes) + XCTAssertEqual(expectedResult.expiresOn, expectedExpiresOn) } else { XCTFail("This method should not be called") } @@ -84,11 +90,11 @@ final class CredentialsDelegateOptionalMethodsNotImplemented: CredentialsDelegat class CredentialsTestValidatorHelper: CredentialsDelegateSpy { func onAccessTokenRetrieveCompleted(_ response: MSALNativeAuthCredentialsController.RefreshTokenCredentialControllerResponse) { - guard case let .success(token) = response.result else { + guard case let .success(result) = response.result else { return XCTFail("wrong result") } - Task { await self.onAccessTokenRetrieveCompleted(accessToken: token) } + Task { await self.onAccessTokenRetrieveCompleted(result: result) } } func onAccessTokenRetrieveError(_ response: MSALNativeAuthCredentialsController.RefreshTokenCredentialControllerResponse) { diff --git a/MSAL/test/unit/native_auth/mock/SignInDelegatesSpies.swift b/MSAL/test/unit/native_auth/mock/SignInDelegatesSpies.swift index d754bc56d7..72e0c9bf2c 100644 --- a/MSAL/test/unit/native_auth/mock/SignInDelegatesSpies.swift +++ b/MSAL/test/unit/native_auth/mock/SignInDelegatesSpies.swift @@ -65,7 +65,6 @@ open class SignInPasswordStartDelegateSpy: SignInStartDelegate { if let expectedUserAccountResult = expectedUserAccountResult { XCTAssertTrue(Thread.isMainThread) XCTAssertEqual(expectedUserAccountResult.idToken, result.idToken) - XCTAssertEqual(expectedUserAccountResult.scopes, result.scopes) } else { XCTFail("This method should not be called") } @@ -106,7 +105,6 @@ class SignInPasswordRequiredDelegateSpy: SignInPasswordRequiredDelegate { XCTAssertTrue(Thread.isMainThread) if let expectedUserAccountResult = expectedUserAccountResult { XCTAssertEqual(expectedUserAccountResult.idToken, result.idToken) - XCTAssertEqual(expectedUserAccountResult.scopes, result.scopes) } else { XCTFail("This method should not be called") } @@ -278,7 +276,6 @@ open class SignInVerifyCodeDelegateSpy: SignInVerifyCodeDelegate { return } XCTAssertEqual(expectedUserAccountResult.idToken, result.idToken) - XCTAssertEqual(expectedUserAccountResult.scopes, result.scopes) XCTAssertTrue(Thread.isMainThread) expectation.fulfill() } @@ -329,7 +326,6 @@ open class SignInAfterSignUpDelegateSpy: SignInAfterSignUpDelegate { return } XCTAssertEqual(expectedUserAccountResult.idToken, result.idToken) - XCTAssertEqual(expectedUserAccountResult.scopes, result.scopes) XCTAssertTrue(Thread.isMainThread) expectation.fulfill() } @@ -361,7 +357,6 @@ class SignInAfterResetPasswordDelegateSpy: SignInAfterResetPasswordDelegate { return } XCTAssertEqual(expectedUserAccountResult.idToken, result.idToken) - XCTAssertEqual(expectedUserAccountResult.scopes, result.scopes) XCTAssertTrue(Thread.isMainThread) expectation.fulfill() } diff --git a/MSAL/test/unit/native_auth/network/responses/validator/MSALNativeAuthTokenResponseValidatorTests.swift b/MSAL/test/unit/native_auth/network/responses/validator/MSALNativeAuthTokenResponseValidatorTests.swift index d983c185fd..8bacca8be0 100644 --- a/MSAL/test/unit/native_auth/network/responses/validator/MSALNativeAuthTokenResponseValidatorTests.swift +++ b/MSAL/test/unit/native_auth/network/responses/validator/MSALNativeAuthTokenResponseValidatorTests.swift @@ -56,16 +56,19 @@ final class MSALNativeAuthTokenResponseValidatorTest: MSALNativeAuthTestCase { func test_whenValidTokenResponse_validationIsSuccessful() { let context = MSALNativeAuthRequestContext(correlationId: defaultUUID) + let accessToken = MSIDAccessToken() + accessToken.accessToken = nil + let refreshToken = MSIDRefreshToken() + refreshToken.refreshToken = nil + let rawIdToken = "rawIdToken" + let authTokens = MSALNativeAuthTokens(accessToken: accessToken, + refreshToken: refreshToken, + rawIdToken: rawIdToken) let userAccountResult = MSALNativeAuthUserAccountResult(account: MSALNativeAuthUserAccountResultStub.account, - authTokens: MSALNativeAuthTokens(accessToken: nil, - refreshToken: nil, - rawIdToken: nil), + authTokens:authTokens, configuration: MSALNativeAuthConfigStubs.configuration, cacheAccessor: MSALNativeAuthCacheAccessorMock()) - let refreshToken = MSIDRefreshToken() - refreshToken.familyId = "familyId" - refreshToken.refreshToken = "refreshToken" let tokenResponse = MSIDCIAMTokenResponse() factory.mockMakeUserAccountResult(userAccountResult) let result = sut.validate(context: context, msidConfiguration: MSALNativeAuthConfigStubs.msidConfiguration, result: .success(tokenResponse)) diff --git a/MSAL/test/unit/native_auth/public/MSALNativeAuthPublicClientApplicationTest.swift b/MSAL/test/unit/native_auth/public/MSALNativeAuthPublicClientApplicationTest.swift index 196ffc0c1a..147fdd6434 100644 --- a/MSAL/test/unit/native_auth/public/MSALNativeAuthPublicClientApplicationTest.swift +++ b/MSAL/test/unit/native_auth/public/MSALNativeAuthPublicClientApplicationTest.swift @@ -585,9 +585,7 @@ final class MSALNativeAuthPublicClientApplicationTest: XCTestCase { let authResultFactoryMock = MSALNativeAuthResultFactoryMock() let userAccountResult = MSALNativeAuthUserAccountResult(account: MSALNativeAuthUserAccountResultStub.account, - authTokens: MSALNativeAuthTokens(accessToken: nil, - refreshToken: nil, - rawIdToken: nil), + authTokens: MSALNativeAuthUserAccountResultStub.authTokens, configuration: MSALNativeAuthConfigStubs.configuration, cacheAccessor: MSALNativeAuthCacheAccessorMock()) authResultFactoryMock.mockMakeUserAccountResult(userAccountResult) @@ -687,9 +685,7 @@ final class MSALNativeAuthPublicClientApplicationTest: XCTestCase { let authResultFactoryMock = MSALNativeAuthResultFactoryMock() let userAccountResult = MSALNativeAuthUserAccountResult(account: MSALNativeAuthUserAccountResultStub.account, - authTokens: MSALNativeAuthTokens(accessToken: nil, - refreshToken: nil, - rawIdToken: nil), + authTokens: MSALNativeAuthUserAccountResultStub.authTokens, configuration: MSALNativeAuthConfigStubs.configuration, cacheAccessor: MSALNativeAuthCacheAccessorMock()) authResultFactoryMock.mockMakeUserAccountResult(userAccountResult) @@ -784,9 +780,7 @@ final class MSALNativeAuthPublicClientApplicationTest: XCTestCase { let authResultFactoryMock = MSALNativeAuthResultFactoryMock() let userAccountResult = MSALNativeAuthUserAccountResult(account: MSALNativeAuthUserAccountResultStub.account, - authTokens: MSALNativeAuthTokens(accessToken: nil, - refreshToken: nil, - rawIdToken: nil), + authTokens: MSALNativeAuthUserAccountResultStub.authTokens, configuration: MSALNativeAuthConfigStubs.configuration, cacheAccessor: MSALNativeAuthCacheAccessorMock()) authResultFactoryMock.mockMakeUserAccountResult(userAccountResult) @@ -874,9 +868,7 @@ final class MSALNativeAuthPublicClientApplicationTest: XCTestCase { let authResultFactoryMock = MSALNativeAuthResultFactoryMock() let userAccountResult = MSALNativeAuthUserAccountResult(account: MSALNativeAuthUserAccountResultStub.account, - authTokens: MSALNativeAuthTokens(accessToken: nil, - refreshToken: nil, - rawIdToken: nil), + authTokens: MSALNativeAuthUserAccountResultStub.authTokens, configuration: MSALNativeAuthConfigStubs.configuration, cacheAccessor: MSALNativeAuthCacheAccessorMock()) authResultFactoryMock.mockMakeUserAccountResult(userAccountResult) @@ -974,9 +966,7 @@ final class MSALNativeAuthPublicClientApplicationTest: XCTestCase { let authResultFactoryMock = MSALNativeAuthResultFactoryMock() let userAccountResult = MSALNativeAuthUserAccountResult(account: MSALNativeAuthUserAccountResultStub.account, - authTokens: MSALNativeAuthTokens(accessToken: nil, - refreshToken: nil, - rawIdToken: nil), + authTokens: MSALNativeAuthUserAccountResultStub.authTokens, configuration: MSALNativeAuthConfigStubs.configuration, cacheAccessor: MSALNativeAuthCacheAccessorMock()) authResultFactoryMock.mockMakeUserAccountResult(userAccountResult) diff --git a/MSAL/test/unit/native_auth/public/MSALNativeAuthUserAccountResultTests.swift b/MSAL/test/unit/native_auth/public/MSALNativeAuthUserAccountResultTests.swift index 73ec596553..3755ad4b75 100644 --- a/MSAL/test/unit/native_auth/public/MSALNativeAuthUserAccountResultTests.swift +++ b/MSAL/test/unit/native_auth/public/MSALNativeAuthUserAccountResultTests.swift @@ -57,26 +57,15 @@ class MSALNativeAuthUserAccountResultTests: XCTestCase { func test_whenAccountAndTokenExist_itReturnsCorrectData() { let expectation = expectation(description: "CredentialsController") - - let mockDelegate = CredentialsDelegateSpy(expectation: expectation, expectedAccessToken: "accessToken") + let authTokens = MSALNativeAuthUserAccountResultStub.authTokens + let mockDelegate = CredentialsDelegateSpy(expectation: expectation, expectedResult: MSALNativeAuthTokenResult(authTokens: authTokens)) + mockDelegate.expectedAccessToken = authTokens.accessToken.accessToken + mockDelegate.expectedExpiresOn = authTokens.accessToken.expiresOn + mockDelegate.expectedScopes = authTokens.accessToken.scopes.array as? [String] ?? [] sut.getAccessToken(delegate: mockDelegate) wait(for: [expectation], timeout: 1) } - func test_whenNoAccessToken_itReturnsCorrectError() { - let expectation = expectation(description: "CredentialsController") - sut = MSALNativeAuthUserAccountResult( - account: account!, - authTokens: MSALNativeAuthTokens(accessToken: nil, refreshToken: nil, rawIdToken: nil), - configuration: MSALNativeAuthConfigStubs.configuration, - cacheAccessor: MSALNativeAuthCacheAccessorMock() - ) - let correlationId = UUID() - let mockDelegate = CredentialsDelegateSpy(expectation: expectation, expectedError: RetrieveAccessTokenError(type: .tokenNotFound, correlationId: correlationId, errorCodes: [])) - sut.getAccessToken(correlationId: correlationId, delegate: mockDelegate) - wait(for: [expectation], timeout: 1) - } - // MARK: - sign-out tests func test_signOut_successfullyCallsCacheAccessor() { diff --git a/MSAL/test/unit/native_auth/public/delegate/DispatchAccessTokenRetrieveCompletedTests.swift b/MSAL/test/unit/native_auth/public/delegate/DispatchAccessTokenRetrieveCompletedTests.swift index a45f69a76a..634c680819 100644 --- a/MSAL/test/unit/native_auth/public/delegate/DispatchAccessTokenRetrieveCompletedTests.swift +++ b/MSAL/test/unit/native_auth/public/delegate/DispatchAccessTokenRetrieveCompletedTests.swift @@ -22,6 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +@_implementationOnly import MSAL_Private @testable import MSAL import XCTest @@ -39,9 +40,19 @@ final class DispatchAccessTokenRetrieveCompletedTests: XCTestCase { } func test_dispatchAccessTokenRetrieveCompleted_whenDelegateMethodsAreImplemented() async { - let expectedToken = "token" - let delegate = CredentialsDelegateSpy(expectation: delegateExp, expectedAccessToken: expectedToken) - + let accessToken = MSIDAccessToken() + accessToken.accessToken = "accessToken" + accessToken.scopes = ["scope1", "scope2"] + let refreshToken = MSIDRefreshToken() + refreshToken.refreshToken = "refreshToken" + let rawIdToken = "rawIdToken" + let authTokens = MSALNativeAuthTokens(accessToken: accessToken, + refreshToken: refreshToken, + rawIdToken: rawIdToken) + let expectedResult = MSALNativeAuthTokenResult(authTokens: authTokens) + let delegate = CredentialsDelegateSpy(expectation: delegateExp, expectedResult: expectedResult) + delegate.expectedAccessToken = accessToken.accessToken + delegate.expectedScopes = accessToken.scopes.array as? [String] ?? [] sut = .init(delegate: delegate, telemetryUpdate: { result in guard case .success = result else { return XCTFail("wrong result") @@ -49,11 +60,11 @@ final class DispatchAccessTokenRetrieveCompletedTests: XCTestCase { self.telemetryExp.fulfill() }) - await sut.dispatchAccessTokenRetrieveCompleted(accessToken: expectedToken, correlationId: correlationId) + await sut.dispatchAccessTokenRetrieveCompleted(result: expectedResult, correlationId: correlationId) await fulfillment(of: [telemetryExp, delegateExp]) - XCTAssertEqual(delegate.expectedAccessToken, expectedToken) + XCTAssertEqual(delegate.expectedResult, expectedResult) } func test_dispatchAccessTokenRetrieveCompleted_whenDelegateOptionalMethodsNotImplemented() async { @@ -69,7 +80,16 @@ final class DispatchAccessTokenRetrieveCompletedTests: XCTestCase { self.telemetryExp.fulfill() }) - await sut.dispatchAccessTokenRetrieveCompleted(accessToken: "token", correlationId: correlationId) + let accessToken = MSIDAccessToken() + accessToken.accessToken = "accessToken" + let refreshToken = MSIDRefreshToken() + refreshToken.refreshToken = "refreshToken" + let rawIdToken = "rawIdToken" + let authTokens = MSALNativeAuthTokens(accessToken: accessToken, + refreshToken: refreshToken, + rawIdToken: rawIdToken) + + await sut.dispatchAccessTokenRetrieveCompleted(result: MSALNativeAuthTokenResult(authTokens: authTokens), correlationId: correlationId) await fulfillment(of: [telemetryExp, delegateExp]) checkError(delegate.expectedError) diff --git a/MSAL/test/unit/native_auth/public/error/RetrieveAccessTokenErrorTests.swift b/MSAL/test/unit/native_auth/public/error/RetrieveAccessTokenErrorTests.swift index 26040a55b8..6ff8fa7ec6 100644 --- a/MSAL/test/unit/native_auth/public/error/RetrieveAccessTokenErrorTests.swift +++ b/MSAL/test/unit/native_auth/public/error/RetrieveAccessTokenErrorTests.swift @@ -30,7 +30,7 @@ final class RetrieveAccessTokenErrorTests: XCTestCase { private var sut: RetrieveAccessTokenError! func test_totalCases() { - XCTAssertEqual(RetrieveAccessTokenError.ErrorType.allCases.count, 4) + XCTAssertEqual(RetrieveAccessTokenError.ErrorType.allCases.count, 3) } func test_customErrorDescription() { @@ -43,14 +43,12 @@ final class RetrieveAccessTokenErrorTests: XCTestCase { let sut: [RetrieveAccessTokenError] = [ .init(type: .browserRequired, correlationId: .init()), .init(type: .refreshTokenExpired, correlationId: .init()), - .init(type: .tokenNotFound, correlationId: .init()), .init(type: .generalError, correlationId: .init()) ] let expectedIdentifiers = [ MSALNativeAuthErrorMessage.browserRequired, MSALNativeAuthErrorMessage.refreshTokenExpired, - MSALNativeAuthErrorMessage.tokenNotFound, MSALNativeAuthErrorMessage.generalError ] @@ -65,20 +63,11 @@ final class RetrieveAccessTokenErrorTests: XCTestCase { sut = .init(type: .browserRequired, correlationId: .init()) XCTAssertTrue(sut.isBrowserRequired) XCTAssertFalse(sut.isRefreshTokenExpired) - XCTAssertFalse(sut.isTokenNotFound) } func test_isRefreshTokenExpired() { sut = .init(type: .refreshTokenExpired, correlationId: .init()) XCTAssertTrue(sut.isRefreshTokenExpired) XCTAssertFalse(sut.isBrowserRequired) - XCTAssertFalse(sut.isTokenNotFound) - } - - func test_isTokenNotFound() { - sut = .init(type: .tokenNotFound, correlationId: .init()) - XCTAssertTrue(sut.isTokenNotFound) - XCTAssertFalse(sut.isBrowserRequired) - XCTAssertFalse(sut.isRefreshTokenExpired) } } diff --git a/azure_pipelines/spm-framework.yml b/azure_pipelines/spm-framework.yml index 3bd9ecc91e..b1f10f531f 100644 --- a/azure_pipelines/spm-framework.yml +++ b/azure_pipelines/spm-framework.yml @@ -36,7 +36,7 @@ jobs: - job: BuildXcFrameworks displayName: Build MSAL framework and release pool: - vmImage: 'macOS-11' + vmImage: 'macOS-13' timeOutInMinutes: 20 steps: @@ -352,11 +352,18 @@ jobs: targetType: 'inline' script: | # Release to CocoaPods - pod trunk push --use-libraries --allow-warnings MSAL.podspec + # Do not use "--use-libraries" option because native auth code doesn't support static library yet + pod trunk push --allow-warnings MSAL.podspec #pod trunk me workingDirectory: '$(Build.SourcesDirectory)' env: COCOAPODS_TRUNK_TOKEN: $(COCOAPODS_TRUNK_TOKEN) + - task: Bash@3 + displayName: Install Sourcekitten + inputs: + targetType: 'inline' + script: | + brew install sourcekitten - task: Bash@3 displayName: Build MSAL docs via Jazzy inputs: diff --git a/build_docs.sh b/build_docs.sh index 0dba505886..f7f7c10c84 100755 --- a/build_docs.sh +++ b/build_docs.sh @@ -13,7 +13,7 @@ cp README.md docs.temp/ echo -e "Generating MSAL documentation" # Generate Swift SourceKitten output -sourcekitten doc -- -workspace MSAL.xcworkspace -scheme "MSAL (iOS Framework)" -configuration Debug RUN_CLANG_STATIC_ANALYZER=NO -sdk iphonesimulator CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO -destination 'platform=iOS Simulator' > docs.temp/swiftDoc.json +sourcekitten doc -- -workspace MSAL.xcworkspace -scheme "MSAL (iOS Framework)" -configuration Debug RUN_CLANG_STATIC_ANALYZER=NO -sdk iphonesimulator CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO -destination 'platform=iOS Simulator,name=iPhone 14,OS=16.4' > docs.temp/swiftDoc.json # Generate Objective-C SourceKitten output cd docs.temp @@ -21,4 +21,4 @@ sourcekitten doc --objc $(pwd)/MSAL/MSAL.h -- -x objective-c -isysroot $(xcrun cd .. # Feed both outputs to Jazzy as a comma-separated list -jazzy --module MSAL --sourcekitten-sourcefile docs.temp/swiftDoc.json,docs.temp/objcDoc.json --author Microsoft\ Corporation --author_url https://aka.ms/azuread --github_url https://github.com/AzureAD/microsoft-authentication-library-for-objc --theme fullwidth \ No newline at end of file +jazzy --module MSAL --sourcekitten-sourcefile docs.temp/swiftDoc.json,docs.temp/objcDoc.json --author Microsoft\ Corporation --author_url https://aka.ms/azuread --github_url https://github.com/AzureAD/microsoft-authentication-library-for-objc --theme fullwidth --output docs.temp/docs