Initialise

To initialise the SDK, there are some pre-requisites:

  • 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.
// create a config object with your api-space-id and an object conforming to CMPAuthenticationDelegate protocol;
CMPComapiConfig *config = [[[[CMPComapiConfig builder] 
   									setApiSpaceID:@"<API_SPACE_ID>"]
   									setAuthDelegate:<CMPAuthenticationDelegate_Conforming_Object>] 										
                    <CMPAuthenticationDelegate_Conforming_Object>build];


CMPComapiClient *client = [CMPComapi initialiseWithConfig:config];
// we can use the client object now
// create a config object with your api-space-id and an object conforming to AuthenticationDelegate protocol;
let config = ComapiConfig.builder()
            .setAuthDelegate(<CMPAuthenticationDelegate_Conforming_Object>)
            .setApiSpaceID("API_SPACE_ID")
            .build()

client = Comapi.initialise(with: config)
guard let client = Comapi.initialise(with: config) else { 
	// initializing error
	return
}
// we can use the client object now

In order 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 must include claims from the authentication panel in the dashboard. To access, so to Channels > Configure and select Authentication in the App Messaging section.

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 */

Learn more about JWTs.

Retrieve the client

The client can be retrieved either as a separate object:

CMPComapiClient *client = [CMPComapi initialiseWithConfig:config];
// client instance ready to use
guard let client = Comapi.initialise(with: config) else {
		// initialisation error
    return
}
// client instance ready to use

or as a singleton:

[CMPComapi initialiseSharedInstanceWithConfig:config];

CMPComapiClient *client = [Comapi shared];
// shared client ready to use
Comapi.initialiseSharedInstance(with: config)

guard let client = Comapi.shared else {
		// initialisation error
    return
}
// shared client ready to use