From 293e9ac71f943fad99eeba2835665f55744c7d95 Mon Sep 17 00:00:00 2001 From: BhupathiArikatla Date: Wed, 15 Apr 2026 15:05:25 +0530 Subject: [PATCH 1/4] added jwt support --- .../MoEngagePluginBase/MoEngagePlugin.swift | 1 + ...ePluginAuthenticationListenerHandler.swift | 43 +++++++++++++ .../MoEngagePluginBridge.swift | 17 ++++++ .../MoEngagePluginConstants.swift | 11 ++++ .../MoEngagePluginUtils.swift | 61 +++++++++++++++++++ 5 files changed, 133 insertions(+) create mode 100644 Sources/MoEngagePluginBase/MoEngagePluginAuthenticationListenerHandler.swift 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..2ed3387 --- /dev/null +++ b/Sources/MoEngagePluginBase/MoEngagePluginAuthenticationListenerHandler.swift @@ -0,0 +1,43 @@ +// +// MoEngagePluginAuthenticationListenerHandler.swift +// MoEngagePlugin +// + +import Foundation +import MoEngageSDK + +#if !os(tvOS) + +@available(iOSApplicationExtension, unavailable) +@available(tvOS, unavailable) +final class MoEngagePluginAuthenticationListenerHandler: NSObject, MoEngageAuthenticationError.Listener { + + private static var handlers = [String: Any]() + + private var identifier: String + + private var messageHandler: MoEngagePluginMessageHandler? { + return MoEngagePluginMessageDelegate.fetchMessageQueueHandler(identifier: identifier) + } + + init(identifier: String) { + self.identifier = identifier + super.init() + registerWithSDK() + } + + private func registerWithSDK() { + MoEngageSDKCore.sharedInstance.registerAuthenticationListener(self, workspaceId: identifier) + MoEngagePluginAuthenticationListenerHandler.handlers[identifier] = self + } + + func onError(_ error: MoEngageAuthenticationError) { + let message = MoEngagePluginUtils.authenticationErrorToJSON(error: error) + messageHandler?.flushMessage( + eventName: MoEngagePluginConstants.CallBackEvents.authenticationError, + message: message + ) + } +} + +#endif diff --git a/Sources/MoEngagePluginBase/MoEngagePluginBridge.swift b/Sources/MoEngagePluginBase/MoEngagePluginBridge.swift index 45c3899..6725d18 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 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..ce1649e 100644 --- a/Sources/MoEngagePluginBase/MoEngagePluginConstants.swift +++ b/Sources/MoEngagePluginBase/MoEngagePluginConstants.swift @@ -42,6 +42,7 @@ public struct MoEngagePluginConstants { static let integrationVersion = "version" public static let analyticsConfig = "analyticsConfig" public static let shouldTrackUserAttributeBooleanAsNumber = "shouldTrackUserAttributeBooleanAsNumber" + public static let emptyString = "" } // user attribute @@ -109,6 +110,15 @@ 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" + } // Callback public struct CallBackEvents { @@ -120,6 +130,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..eac403f 100644 --- a/Sources/MoEngagePluginBase/MoEngagePluginUtils.swift +++ b/Sources/MoEngagePluginBase/MoEngagePluginUtils.swift @@ -169,6 +169,67 @@ public class MoEngagePluginUtils { MoEngagePluginConstants.General.accountMeta: createAccountPayload(identifier: identifier) ] } + +#if !os(tvOS) + static func authenticationErrorToJSON(error: MoEngageAuthenticationError) -> [String: Any] { + let accountMeta = createAccountPayload(identifier: error.accountMeta.appID) + var dataPayload = [String: Any]() + + if let jwtError = error as? MoEngageJwtAuthenticationError { + dataPayload[MoEngagePluginConstants.Authentication.errorCode] = jwtErrorCodeString(for: jwtError) + dataPayload[MoEngagePluginConstants.General.message] = jwtError.details.message ?? MoEngagePluginConstants.General.emptyString + dataPayload[MoEngagePluginConstants.Authentication.token] = jwtError.details.token ?? MoEngagePluginConstants.General.emptyString + dataPayload[MoEngagePluginConstants.Authentication.userIdentifier] = jwtError.details.identifier ?? MoEngagePluginConstants.General.emptyString + } + + return [ + MoEngagePluginConstants.General.accountMeta: accountMeta, + MoEngagePluginConstants.General.platform: MoEngagePluginConstants.General.iOS, + MoEngagePluginConstants.Authentication.authType:MoEngagePluginConstants.Authentication.jwt, + MoEngagePluginConstants.General.data: dataPayload + ] + } + + private static func jwtErrorCodeString(for jwtError: MoEngageJwtAuthenticationError) -> String { + switch jwtError.details.code { + case .timeConstraintFailure: + return "TIME_CONSTRAINT_FAILURE" + case .decryptionFailed: + return "DECRYPTION_FAILED" + case .headerTypeIncompatible: + return "HEADER_TYPE_INCOMPATIBLE" + case .tokenPayloadContentMissing: + return "PAYLOAD_CONTENT_MISSING" + case .invalidSignature: + return "INVALID_SIGNATURE" + case .identifierMismatch: + return "IDENTIFIER_MISMATCH" + case .tokenNotAvailable: + return "TOKEN_NOT_AVAILABLE" + case .unknown: + return "UNKNOWN" + } + } +#endif + static func authPayloadToNativeModel(payload: [String: Any]) -> MoEngageAuthenticationDetails? { + guard let authData = payload[MoEngagePluginConstants.General.data] as? [String: Any] else { + return nil + } + guard let authType = authData[MoEngagePluginConstants.Authentication.authType] as? String, + let token = authData[MoEngagePluginConstants.Authentication.token] as? String, + let userIdentifier = authData[MoEngagePluginConstants.Authentication.userIdentifier] as? String + else { + MoEngageLogger.logDefault(logLevel: .error, message: "Authentication details missing required fields: type, token, or identifier") + return nil + } + if authType == MoEngagePluginConstants.Authentication.jwt{ + return MoEngageJwtAuthenticationDetails(token: token, identifier: userIdentifier) + } + else { + MoEngageLogger.logDefault(logLevel: .error, message: "Authentication type '\(authType)' is not supported.") + return nil + } + } } extension MoEngageInAppCampaign { From 0e8c1bdab0dcbee3126cee02c6f71f0393a30572 Mon Sep 17 00:00:00 2001 From: BhupathiArikatla Date: Sat, 18 Apr 2026 14:00:27 +0530 Subject: [PATCH 2/4] Added the changelog --- CHANGELOG.md | 6 ++++++ Sources/MoEngagePluginBase/MoEngagePluginUtils.swift | 4 +--- 2 files changed, 7 insertions(+), 3 deletions(-) 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/MoEngagePluginUtils.swift b/Sources/MoEngagePluginBase/MoEngagePluginUtils.swift index eac403f..4051607 100644 --- a/Sources/MoEngagePluginBase/MoEngagePluginUtils.swift +++ b/Sources/MoEngagePluginBase/MoEngagePluginUtils.swift @@ -170,7 +170,6 @@ public class MoEngagePluginUtils { ] } -#if !os(tvOS) static func authenticationErrorToJSON(error: MoEngageAuthenticationError) -> [String: Any] { let accountMeta = createAccountPayload(identifier: error.accountMeta.appID) var dataPayload = [String: Any]() @@ -210,12 +209,11 @@ public class MoEngagePluginUtils { return "UNKNOWN" } } -#endif static func authPayloadToNativeModel(payload: [String: Any]) -> MoEngageAuthenticationDetails? { guard let authData = payload[MoEngagePluginConstants.General.data] as? [String: Any] else { return nil } - guard let authType = authData[MoEngagePluginConstants.Authentication.authType] as? String, + guard let authType = payload[MoEngagePluginConstants.Authentication.authType] as? String, let token = authData[MoEngagePluginConstants.Authentication.token] as? String, let userIdentifier = authData[MoEngagePluginConstants.Authentication.userIdentifier] as? String else { From e42870b29c785826db1a7d77d7367c7947bf29b2 Mon Sep 17 00:00:00 2001 From: BhupathiArikatla Date: Sat, 18 Apr 2026 14:03:30 +0530 Subject: [PATCH 3/4] available in tvos --- .../MoEngagePluginAuthenticationListenerHandler.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/Sources/MoEngagePluginBase/MoEngagePluginAuthenticationListenerHandler.swift b/Sources/MoEngagePluginBase/MoEngagePluginAuthenticationListenerHandler.swift index 2ed3387..7fddc8f 100644 --- a/Sources/MoEngagePluginBase/MoEngagePluginAuthenticationListenerHandler.swift +++ b/Sources/MoEngagePluginBase/MoEngagePluginAuthenticationListenerHandler.swift @@ -6,10 +6,8 @@ import Foundation import MoEngageSDK -#if !os(tvOS) @available(iOSApplicationExtension, unavailable) -@available(tvOS, unavailable) final class MoEngagePluginAuthenticationListenerHandler: NSObject, MoEngageAuthenticationError.Listener { private static var handlers = [String: Any]() @@ -40,4 +38,3 @@ final class MoEngagePluginAuthenticationListenerHandler: NSObject, MoEngageAuthe } } -#endif From 0a7f50ea54ac484d094bf2afb97c4a2aae5be1f5 Mon Sep 17 00:00:00 2001 From: BhupathiArikatla Date: Mon, 27 Apr 2026 17:20:18 +0530 Subject: [PATCH 4/4] Addressed the comments --- ...ePluginAuthenticationListenerHandler.swift | 20 +++--- .../MoEngagePluginBridge.swift | 2 +- .../MoEngagePluginConstants.swift | 11 ++- .../MoEngagePluginUtils.swift | 72 ++++++++++--------- 4 files changed, 58 insertions(+), 47 deletions(-) diff --git a/Sources/MoEngagePluginBase/MoEngagePluginAuthenticationListenerHandler.swift b/Sources/MoEngagePluginBase/MoEngagePluginAuthenticationListenerHandler.swift index 7fddc8f..47adfee 100644 --- a/Sources/MoEngagePluginBase/MoEngagePluginAuthenticationListenerHandler.swift +++ b/Sources/MoEngagePluginBase/MoEngagePluginAuthenticationListenerHandler.swift @@ -10,9 +10,7 @@ import MoEngageSDK @available(iOSApplicationExtension, unavailable) final class MoEngagePluginAuthenticationListenerHandler: NSObject, MoEngageAuthenticationError.Listener { - private static var handlers = [String: Any]() - - private var identifier: String + private let identifier: String private var messageHandler: MoEngagePluginMessageHandler? { return MoEngagePluginMessageDelegate.fetchMessageQueueHandler(identifier: identifier) @@ -21,20 +19,20 @@ final class MoEngagePluginAuthenticationListenerHandler: NSObject, MoEngageAuthe init(identifier: String) { self.identifier = identifier super.init() - registerWithSDK() + registerListenerInSDK() } - private func registerWithSDK() { + private func registerListenerInSDK() { MoEngageSDKCore.sharedInstance.registerAuthenticationListener(self, workspaceId: identifier) - MoEngagePluginAuthenticationListenerHandler.handlers[identifier] = self } func onError(_ error: MoEngageAuthenticationError) { - let message = MoEngagePluginUtils.authenticationErrorToJSON(error: error) - messageHandler?.flushMessage( - eventName: MoEngagePluginConstants.CallBackEvents.authenticationError, - message: message - ) + 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 6725d18..ef26e9e 100644 --- a/Sources/MoEngagePluginBase/MoEngagePluginBridge.swift +++ b/Sources/MoEngagePluginBase/MoEngagePluginBridge.swift @@ -252,7 +252,7 @@ import MoEngageInApps } else { MoEngageLogger.logDefault( logLevel: .error, - message: "Failed to map Authentication details or missing AppID" + message: "Failed to map Authentication details unsupported type or missing AppID" ) } } diff --git a/Sources/MoEngagePluginBase/MoEngagePluginConstants.swift b/Sources/MoEngagePluginBase/MoEngagePluginConstants.swift index ce1649e..5787df0 100644 --- a/Sources/MoEngagePluginBase/MoEngagePluginConstants.swift +++ b/Sources/MoEngagePluginBase/MoEngagePluginConstants.swift @@ -42,7 +42,6 @@ public struct MoEngagePluginConstants { static let integrationVersion = "version" public static let analyticsConfig = "analyticsConfig" public static let shouldTrackUserAttributeBooleanAsNumber = "shouldTrackUserAttributeBooleanAsNumber" - public static let emptyString = "" } // user attribute @@ -118,6 +117,16 @@ public struct MoEngagePluginConstants { 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 diff --git a/Sources/MoEngagePluginBase/MoEngagePluginUtils.swift b/Sources/MoEngagePluginBase/MoEngagePluginUtils.swift index 4051607..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,61 +169,65 @@ public class MoEngagePluginUtils { MoEngagePluginConstants.General.accountMeta: createAccountPayload(identifier: identifier) ] } - - static func authenticationErrorToJSON(error: MoEngageAuthenticationError) -> [String: Any] { + + static func authenticationErrorToJSON(error: MoEngageAuthenticationError) -> [String: Any]? { let accountMeta = createAccountPayload(identifier: error.accountMeta.appID) var dataPayload = [String: Any]() - - if let jwtError = error as? MoEngageJwtAuthenticationError { - dataPayload[MoEngagePluginConstants.Authentication.errorCode] = jwtErrorCodeString(for: jwtError) - dataPayload[MoEngagePluginConstants.General.message] = jwtError.details.message ?? MoEngagePluginConstants.General.emptyString - dataPayload[MoEngagePluginConstants.Authentication.token] = jwtError.details.token ?? MoEngagePluginConstants.General.emptyString - dataPayload[MoEngagePluginConstants.Authentication.userIdentifier] = jwtError.details.identifier ?? MoEngagePluginConstants.General.emptyString - } + 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.Authentication.authType:MoEngagePluginConstants.Authentication.jwt, MoEngagePluginConstants.General.data: dataPayload ] } - + private static func jwtErrorCodeString(for jwtError: MoEngageJwtAuthenticationError) -> String { + switch jwtError.details.code { case .timeConstraintFailure: - return "TIME_CONSTRAINT_FAILURE" + return MoEngagePluginConstants.Authentication.Error.timeConstraintFailure case .decryptionFailed: - return "DECRYPTION_FAILED" + return MoEngagePluginConstants.Authentication.Error.decryptionFailed case .headerTypeIncompatible: - return "HEADER_TYPE_INCOMPATIBLE" + return MoEngagePluginConstants.Authentication.Error.headerTypeIncompatible case .tokenPayloadContentMissing: - return "PAYLOAD_CONTENT_MISSING" + return MoEngagePluginConstants.Authentication.Error.tokenPayloadContentMissing case .invalidSignature: - return "INVALID_SIGNATURE" + return MoEngagePluginConstants.Authentication.Error.invalidSignature case .identifierMismatch: - return "IDENTIFIER_MISMATCH" + return MoEngagePluginConstants.Authentication.Error.identifierMismatch case .tokenNotAvailable: - return "TOKEN_NOT_AVAILABLE" + return MoEngagePluginConstants.Authentication.Error.tokenNotAvailable case .unknown: - return "UNKNOWN" + return MoEngagePluginConstants.Authentication.Error.unknown } } static func authPayloadToNativeModel(payload: [String: Any]) -> MoEngageAuthenticationDetails? { - guard let authData = payload[MoEngagePluginConstants.General.data] as? [String: Any] else { - return nil - } - guard let authType = payload[MoEngagePluginConstants.Authentication.authType] as? String, - let token = authData[MoEngagePluginConstants.Authentication.token] as? String, - let userIdentifier = authData[MoEngagePluginConstants.Authentication.userIdentifier] as? String + 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 required fields: type, token, or identifier") + MoEngageLogger.logDefault(logLevel: .error, message: "Authentication details missing 'data' or 'authType'") return nil } - if authType == MoEngagePluginConstants.Authentication.jwt{ + 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 { + } else { MoEngageLogger.logDefault(logLevel: .error, message: "Authentication type '\(authType)' is not supported.") return nil }