diff --git a/CHANGELOG.md b/CHANGELOG.md index 56a585a..38a3a90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# Release Date + +## Release Version + +- [patch] MOEN-44062: Added jwt support + # 07-04-2026 ## 6.8.2 diff --git a/Sources/MoEngagePluginBase/MoEngagePlugin.swift b/Sources/MoEngagePluginBase/MoEngagePlugin.swift index 0779782..082addf 100644 --- a/Sources/MoEngagePluginBase/MoEngagePlugin.swift +++ b/Sources/MoEngagePluginBase/MoEngagePlugin.swift @@ -147,6 +147,7 @@ extension MoEngagePlugin: MoEngageModule.Item { MoEngageLogger.logDefault(message: "MoEngagePluginMessageDelegateHandler is unavailable for tvOS 🛑") #else _ = MoEngagePluginMessageDelegateHandler(identifier: identifier) + _ = MoEngagePluginAuthenticationListenerHandler(identifier: identifier) #endif MoEngagePluginBaseHandler.initializePluginBridge(className: MoEngagePluginConstants.ExternalPluginBase.cardsBridge) } diff --git a/Sources/MoEngagePluginBase/MoEngagePluginAuthenticationListenerHandler.swift b/Sources/MoEngagePluginBase/MoEngagePluginAuthenticationListenerHandler.swift new file mode 100644 index 0000000..47adfee --- /dev/null +++ b/Sources/MoEngagePluginBase/MoEngagePluginAuthenticationListenerHandler.swift @@ -0,0 +1,38 @@ +// +// MoEngagePluginAuthenticationListenerHandler.swift +// MoEngagePlugin +// + +import Foundation +import MoEngageSDK + + +@available(iOSApplicationExtension, unavailable) +final class MoEngagePluginAuthenticationListenerHandler: NSObject, MoEngageAuthenticationError.Listener { + + private let identifier: String + + private var messageHandler: MoEngagePluginMessageHandler? { + return MoEngagePluginMessageDelegate.fetchMessageQueueHandler(identifier: identifier) + } + + init(identifier: String) { + self.identifier = identifier + super.init() + registerListenerInSDK() + } + + private func registerListenerInSDK() { + MoEngageSDKCore.sharedInstance.registerAuthenticationListener(self, workspaceId: identifier) + } + + func onError(_ error: MoEngageAuthenticationError) { + if let message = MoEngagePluginUtils.authenticationErrorToJSON(error: error) { + messageHandler?.flushMessage( + eventName: MoEngagePluginConstants.CallBackEvents.authenticationError, + message: message + ) + } + } +} + diff --git a/Sources/MoEngagePluginBase/MoEngagePluginBridge.swift b/Sources/MoEngagePluginBase/MoEngagePluginBridge.swift index 45c3899..ef26e9e 100644 --- a/Sources/MoEngagePluginBase/MoEngagePluginBridge.swift +++ b/Sources/MoEngagePluginBase/MoEngagePluginBridge.swift @@ -240,6 +240,23 @@ import MoEngageInApps #endif } + @objc public func passAuthenticationDetails(_ authenticationDetails: [String: Any]) { + if let identifier = MoEngagePluginUtils.fetchIdentifierFromPayload(attribute: authenticationDetails), + let authDetails = MoEngagePluginUtils.authPayloadToNativeModel(payload: authenticationDetails) { + MoEngageSDKCore.sharedInstance.passAuthenticationDetails(authDetails, workspaceId: identifier) + + MoEngageLogger.logDefault( + logLevel: .verbose, + message: "Authentication details passed for identifier: \(identifier)" + ) + } else { + MoEngageLogger.logDefault( + logLevel: .error, + message: "Failed to map Authentication details unsupported type or missing AppID" + ) + } + } + // MARK: Other @objc public func validateSDKVersion() -> Bool { if MoEngagePluginConstants.SDKVersions.currentVersion >= MoEngagePluginConstants.SDKVersions.minimumVersion && MoEngagePluginConstants.SDKVersions.currentVersion < MoEngagePluginConstants.SDKVersions.maximumVersion { diff --git a/Sources/MoEngagePluginBase/MoEngagePluginConstants.swift b/Sources/MoEngagePluginBase/MoEngagePluginConstants.swift index 09a2300..5787df0 100644 --- a/Sources/MoEngagePluginBase/MoEngagePluginConstants.swift +++ b/Sources/MoEngagePluginBase/MoEngagePluginConstants.swift @@ -109,6 +109,25 @@ public struct MoEngagePluginConstants { static let screenName = "screenName" static let clickedAction = "clickedAction" } + + public struct Authentication { + public static let authType = "authenticationType" + public static let errorCode = "code" + public static let errorMessage = "message" + public static let token = "token" + public static let userIdentifier = "userIdentifier" + public static let jwt = "JWT" + public struct Error { + public static let timeConstraintFailure = "TIME_CONSTRAINT_FAILURE" + public static let decryptionFailed = "DECRYPTION_FAILED" + public static let headerTypeIncompatible = "HEADER_TYPE_INCOMPATIBLE" + public static let tokenPayloadContentMissing = "PAYLOAD_CONTENT_MISSING" + public static let invalidSignature = "INVALID_SIGNATURE" + public static let identifierMismatch = "IDENTIFIER_MISMATCH" + public static let tokenNotAvailable = "TOKEN_NOT_AVAILABLE" + public static let unknown = "UNKNOWN" + } + } // Callback public struct CallBackEvents { @@ -120,6 +139,7 @@ public struct MoEngagePluginConstants { public static let pushTokenGenerated = "MoEPushTokenGenerated" public static let pushClicked = "MoEPushClicked" public static let logOutCompleted = "MoELogoutComplete" + public static let authenticationError = "MoEAuthenticationError" } struct ExternalPluginBase { diff --git a/Sources/MoEngagePluginBase/MoEngagePluginUtils.swift b/Sources/MoEngagePluginBase/MoEngagePluginUtils.swift index abe6b0d..b186b3a 100644 --- a/Sources/MoEngagePluginBase/MoEngagePluginUtils.swift +++ b/Sources/MoEngagePluginBase/MoEngagePluginUtils.swift @@ -134,7 +134,7 @@ public class MoEngagePluginUtils { static func mapSelfHandledCampaignDataToJSON(campaignData: MoEngageInAppSelfHandledData) -> [String: Any] { let accountMeta = campaignData.accountMeta let accountPaylaod = createAccountPayload(identifier: accountMeta.appID) - + let campaignsPayload = campaignData.campaigns.map { campaign in let selfHandledPayload = mapSelfHandledCampaignToJSON(campaign) return [MoEngagePluginConstants.General.platform: MoEngagePluginConstants.General.iOS, @@ -150,17 +150,17 @@ public class MoEngagePluginUtils { MoEngagePluginConstants.InApp.screenName: rules.screenName, MoEngagePluginConstants.InApp.contexts: rules.contexts] } - + static func mapSelfHandledCampaignToJSON(_ campaign: MoEngageInAppSelfHandledCampaign) -> [String: Any] { var campaignPayload = campaign.fetchInAppPaylaod() - + var selfHandledPayload = [String: Any]() selfHandledPayload[MoEngagePluginConstants.General.payload] = campaign.campaignContent selfHandledPayload[MoEngagePluginConstants.InApp.dismissInterval] = campaign.autoDismissInterval selfHandledPayload[MoEngagePluginConstants.InApp.displayRules] = mapDisplayRulesToJSON(campaign.displayRules) - + campaignPayload[MoEngagePluginConstants.InApp.selfHandled] = selfHandledPayload - + return campaignPayload } @@ -169,6 +169,69 @@ public class MoEngagePluginUtils { MoEngagePluginConstants.General.accountMeta: createAccountPayload(identifier: identifier) ] } + + static func authenticationErrorToJSON(error: MoEngageAuthenticationError) -> [String: Any]? { + let accountMeta = createAccountPayload(identifier: error.accountMeta.appID) + var dataPayload = [String: Any]() + + guard let jwtError = error as? MoEngageJwtAuthenticationError else { + MoEngageLogger.logDefault(message: "Other type of authentication is not supported in the sdk") + return nil + + } + dataPayload[MoEngagePluginConstants.Authentication.authType] = MoEngagePluginConstants.Authentication.jwt + dataPayload[MoEngagePluginConstants.Authentication.errorCode] = jwtErrorCodeString(for: jwtError) + dataPayload[MoEngagePluginConstants.General.message] = jwtError.details.message ?? "" + dataPayload[MoEngagePluginConstants.Authentication.token] = jwtError.details.token ?? "" + dataPayload[MoEngagePluginConstants.Authentication.userIdentifier] = jwtError.details.identifier ?? "" + return [ + MoEngagePluginConstants.General.accountMeta: accountMeta, + MoEngagePluginConstants.General.platform: MoEngagePluginConstants.General.iOS, + MoEngagePluginConstants.General.data: dataPayload + ] + } + + private static func jwtErrorCodeString(for jwtError: MoEngageJwtAuthenticationError) -> String { + + switch jwtError.details.code { + case .timeConstraintFailure: + return MoEngagePluginConstants.Authentication.Error.timeConstraintFailure + case .decryptionFailed: + return MoEngagePluginConstants.Authentication.Error.decryptionFailed + case .headerTypeIncompatible: + return MoEngagePluginConstants.Authentication.Error.headerTypeIncompatible + case .tokenPayloadContentMissing: + return MoEngagePluginConstants.Authentication.Error.tokenPayloadContentMissing + case .invalidSignature: + return MoEngagePluginConstants.Authentication.Error.invalidSignature + case .identifierMismatch: + return MoEngagePluginConstants.Authentication.Error.identifierMismatch + case .tokenNotAvailable: + return MoEngagePluginConstants.Authentication.Error.tokenNotAvailable + case .unknown: + return MoEngagePluginConstants.Authentication.Error.unknown + } + } + static func authPayloadToNativeModel(payload: [String: Any]) -> MoEngageAuthenticationDetails? { + guard let authData = payload[MoEngagePluginConstants.General.data] as? [String: Any], + let authType = authData[MoEngagePluginConstants.Authentication.authType] as? String + else { + MoEngageLogger.logDefault(logLevel: .error, message: "Authentication details missing 'data' or 'authType'") + return nil + } + if authType == MoEngagePluginConstants.Authentication.jwt { + guard let token = authData[MoEngagePluginConstants.Authentication.token] as? String, + let userIdentifier = authData[MoEngagePluginConstants.Authentication.userIdentifier] as? String + else { + MoEngageLogger.logDefault(logLevel: .error, message: "JWT Authentication details missing token or identifier") + return nil + } + return MoEngageJwtAuthenticationDetails(token: token, identifier: userIdentifier) + } else { + MoEngageLogger.logDefault(logLevel: .error, message: "Authentication type '\(authType)' is not supported.") + return nil + } + } } extension MoEngageInAppCampaign {