Initialise

To initialise the Comapi SDK, you will need a few pre-requisites listed below:

  • A configured API Space
  • An authentication provider that can generate a JWT that matches the auth scheme configured for your Api Space.
  • The generated JWT must include the provided nonce as a claim in the generated JWT
  • A configured object conforming to CMPStoreFactoryBuildable protocol which returns a valid CMPChatStore implementation object
// create an instance of CMPStoreFactoryBuildable implementing class and keep reference to it somewhere
ChatStoreFactoryImplementation *factory = [ChatStoreFactoryImplementation alloc] init];

// create a config builder
CMPComapiConfigBuilder<CMPChatConfig *> *builder = [CMPChatConfig builder];

// create a config object with your api-space-id and an object conforming to CMPAuthenticationDelegate protocol;
CMPChatConfig *config = [[[[[builder setApiSpaceID:<YOUR_API_SPACE_ID>]
                 setAuthDelegate:<CMPAuthenticationDelegate_Conforming_Object>]                    setChatStoreFactory:factory] 
                 build];

[CMPChat initialiseWithConfig:config completion:^(CMPComapiChatClient * _Nullable client) {
    if (client) {
        // we can use the client object now
    }
}];
// create an instance of CMPStoreFactoryBuildable implementing class and keep reference to it somewhere
let factory = ChatStoreFactoryImplementation()

// create a config builder
let builder = ChatConfig.builder()

// create a config object with your api-space-id and an object conforming to AuthenticationDelegate protocol;
let config = builder
            .setAuthDelegate(<CMPAuthenticationDelegate_Conforming_Object>)
            .setApiSpaceID(<YOUR_API_SPACE_ID>)
                        .setChatStoreFactory(factory)
            .build()

Chat.initialise(with: config) { client in
  if client != nil {
    // we can use the client object now
  }
}

In order for the client to be able to start a session, the config's authenticationDelegate object must conform to the protocol's method:

NSString *id = <Portal's authentication tab ID claim value>;
NSString *issuer = <Portal's authentication tab issuer value>;
NSString *audience = <Portal's authentication tab audience value>;
NSString *secret = <Portal's authentication tab secret value>;
  
- (void)client:(CMPComapiClient *)client didReceiveAuthenticationChallenge:(CMPAuthenticationChallenge *)challenge completion:(void (^)(NSString * _Nullable))continueWithToken {
    // request a JWT token from your provider (backend server)
    // example call
    [YourProviderServer getTokenForNonce:challenge.nonce id:id issuer:issuer audience:audience secret:secret completion:^(NSString * token, NSError * error) {
            // call continueWithToken block with generated token
        if (token && !error) {
            continueWithToken(token);
        }
    }];
}
let id: String = <Portal's authentication tab ID claim value>
let issuer: String = <Portal's authentication tab issuer value>
let audience: String = <Portal's authentication tab audience value>
let secret: String = <Portal's authentication tab secret value>

func client(_ client: ComapiClient, didReceive challenge:               AuthenticationChallenge, completion continueWithToken: @escaping (String?) -> Void) {
        // request a JWT token from your provider (backend server)
    // example call
    YourProviderServer.getToken(nonce: challenge.nonce, id: id, issuer: issuer,          audience: audience, completion: { token: String?, error: Error? in
            // call continueWithToken block with generated token
        if (token != nil && error == nil) {
            continueWithToken(token!);
        }
    })
}

The JWT token needs to include claims from the authentication panel in the dashboard, which can be found at Channels -> App Messaging.

Here's an example implementation of a token generator in Objective-C and Swift using JWT

#import "CMPAuthenticationManager.h"
#import <JWT/JWT.h>

@implementation CMPAuthenticationManager

+ (NSString *)generateTokenForNonce:(NSString *)nonce profileID:(NSString *)profileID issuer:(NSString *)issuer audience:(NSString *)audience secret:(NSString *)secret {
    NSDate *now = [NSDate date];
    NSDate *exp = [NSCalendar.currentCalendar dateByAddingUnit:NSCalendarUnitDay value:30 toDate:now options:0];
    
    NSDictionary *headers = @{@"typ" : @"JWT"};
    NSDictionary *payload = @{@"nonce" : nonce,
                               @"sub" : profileID,
                               @"iss" : issuer,
                               @"aud" : audience,
                               @"iat" : [NSNumber numberWithDouble:now.timeIntervalSince1970],
                               @"exp" : [NSNumber numberWithDouble:exp.timeIntervalSince1970]};
    
    NSData *secretData = [secret dataUsingEncoding:NSUTF8StringEncoding];
    id<JWTAlgorithm> algorithm = [JWTAlgorithmFactory algorithmByName:@"HS256"];
    
    NSString *token = [JWTBuilder encodePayload:payload].headers(headers).secretData(secretData).algorithm(algorithm).encode;
    return token;
}

@end
  
/* Note that this should preferably be generated by your backend, the app should only retreive the token through an HTTP call */
import JWT

class JWTokenGenerator {
    
    struct AuthHeaders {
        static let HeaderType = "JWT"
    }
    
    static func generate(tokenFor nonce: String, profileId: String, issuer: String, audience: String, secret: String) -> String {
        let now = Date()
        let exp = Calendar.current.date(byAdding: .day, value: 30, to: now)!
        
        let base64SecretKey = secret.data(using: .utf8)!
        
        let headers = ["typ" : NSString.init(string: AuthHeaders.HeaderType)] as [AnyHashable : Any]
        
        let claims = ["nonce" : NSString.init(string: nonce),
                      "sub" : NSString.init(string: profileId),
                      "iss" : NSString.init(string: issuer),
                      "aud" : NSString.init(string: audience),
                      "iat" : NSNumber(value: now.timeIntervalSince1970),
                      "exp" : NSNumber(value: exp.timeIntervalSince1970)] as [AnyHashable : Any]
        
        let algorithm = JWTAlgorithmFactory.algorithm(byName: "HS256")
        
        let e = JWTBuilder.encodePayload(claims)!
        
        let h = e.headers(headers)!
        let s = h.secretData(base64SecretKey)!
        let b = s.algorithm(algorithm)!
        let token = b.encode

        return token!
    }
}

/* Note that this should preferably be generated by your backend, the app should only retreive the token through an HTTP call */

For more information on JWT visit this link.