To initialise the SDK, you must set up a ComapiChatConfig
object, create an instance of the SDK, and then call the initialise method passing in the config object.
ES6
import { IAuthChallengeOptions, LogPersistences, OrphanedEventPersistences } from '@comapi/sdk-js-foundation';
import { ComapiChatClient, ComapiChatConfig } from '@comapi/sdk-js-chat';
// We will discuss this in it's own section
import { ConversationStore } from '???';
export class ComapiService {
private chatClient: ComapiChatClient;
private comapiConfig: ComapiChatConfig;
constructor(private _authService: AuthService, private _conversationStore ConversationStore) {
// create / initialise ComapiChatConfig
this.comapiConfig = new ComapiChatConfig()
.withStore(_conversationStore)
.withApiSpace(">> YOUR APP SPACE ID <<<")
.withAuthChallenge(this.authChallenge.bind(this));
}
/**
* Auth Challenge.
*/
private authChallenge(options: IAuthChallengeOptions, answerAuthenticationChallenge) {
this._authService.getToken(options.nonce)
.then((token) => {
answerAuthenticationChallenge(token);
});
}
public initialise(): Promise<boolean> {
if (this.chatClient) {
return Promise.resolve(false);
} else {
this.chatClient = new ComapiChatClient();
return this.chatClient.initialise(this.comapiConfig);
}
}
public uninitialise(): Promise<boolean> {
return this.chatClient.uninitialise()
.then(function () {
this.chatClient = undefined;
return true;
});
}
}
Advanced options
The above examples initialised the SDK with minimal configuration. You can customise the SDK behaviour with the following optional settings.
- withEventPageSize(eventPageSize: number)
Defaults to 10.
If a gap in the conversation messages is detected, the SDK fills this by querying the missing events in pages until the gap is filled. This parameter represents the page size. - withMessagePageSize(messagePageSize: number)
Defaults to 10. - withLazyLoadThreshold(lazyLoadThreshold: number)
Defaults to 1. - withMaxEventGap(maxEventGap: number)
Defaults to 100. - withAutoSynchronize(autoSynchronize: boolean)
Defaults to true.
Authentication
JWT
The Auth Challenge needs to generate and return a JWT using the answerAuthenticationChallenge
method.
There are four pieces of data that must be specified in the portal for the Api space auth settings:
- Issuer
- Audience
- Shared secret
- ID claim
A cryptographic nonce is used as part of the authentication flow. This is passed into the authChallenge
(options.nonce
) and must be added as a claim in the generated JWT.
The below sample uses jsrsasign to dynamically create a client side JWT:
function authChallenge (options, answerAuthenticationChallenge) {
// Header
var oHeader = { alg: 'HS256', typ: 'JWT' };
// Payload
var tNow = KJUR.jws.IntDate.get('now');
var tEnd = KJUR.jws.IntDate.get('now + 1day');
var oPayload = {
sub: "john smith",
nonce: options.nonce,
iss: "https://my-issuer.com",
aud: "https://my-audience.com",
iat: tNow,
exp: tEnd,
};
var sHeader = JSON.stringify(oHeader);
var sPayload = JSON.stringify(oPayload);
var sJWT = KJUR.jws.JWS.sign("HS256", sHeader, sPayload, "my shared secret");
answerAuthenticationChallenge(sJWT);
}
This node express method uses the njwt package and achieves the same as above but server-side:
/**
* @Params {string} req.body.username
* @Params {string} req.body.password
* @Params {string} req.body.nonce
*/
app.post('/authenticate', function (req, res, next) {
// TODO: authenticate username & password ...
var claims = {
iss: "https://my-issuer.com",
sub: req.body.username,
nonce: req.body.nonce,
aud: "https://my-audience.com"
}
var jwt = njwt.create(claims, "my shared secret");
var token = jwt.compact();
res.json({ jwt: token });
});
The following auth challenge could be used in conjunction with the above node endpoint:
function authChallenge (options, answerAuthenticationChallenge) {
$http.post("/authenticate", {
username: "johnSmith"
password: "Passw0rd!",
nonce: options.nonce })
.then(function (response) {
answerAuthenticationChallenge(response.data.token);
})
.catch(function (error) {
answerAuthenticationChallenge(null);
});
}