Skip to content

Commit

Permalink
feat: support user hmac hash generation
Browse files Browse the repository at this point in the history
  • Loading branch information
Ephraim Nartey committed Nov 29, 2024
1 parent b76a707 commit 3d2de1c
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 8 deletions.
26 changes: 23 additions & 3 deletions lib/chatwoot_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:chatwoot_sdk/di/modules.dart';
import 'package:chatwoot_sdk/chatwoot_parameters.dart';
import 'package:chatwoot_sdk/repository_parameters.dart';
import 'package:riverpod/riverpod.dart';
import 'package:uuid/uuid.dart';

import 'data/local/local_storage.dart';

Expand Down Expand Up @@ -93,7 +94,8 @@ class ChatwootClient {
/// handling chatwoot events. By default persistence is enabled, to disable persistence set [enablePersistence] as false
static Future<ChatwootClient> create(
{required String baseUrl,
required String inboxIdentifier,
required String inboxIdentifier,
String? userIdentityValidationKey,
ChatwootUser? user,
bool enablePersistence = true,
ChatwootCallbacks? callbacks}) async {
Expand All @@ -109,10 +111,28 @@ class ChatwootClient {
isPersistenceEnabled: enablePersistence,
baseUrl: baseUrl,
inboxIdentifier: inboxIdentifier,
userIdentifier: user?.identifier);
userIdentityValidationKey: userIdentityValidationKey);

ChatwootUser? chatUser = user;
if(userIdentityValidationKey != null && user != null ){
final userIdentifier = user.identifier ?? user.email ?? Uuid().v4();
final identifierHash = chatwootParams.generateHmacHash(userIdentityValidationKey, userIdentifier);
chatUser = ChatwootUser(
identifier: userIdentifier,
identifierHash: identifierHash,
name: user.name,
email: user.email,
avatarUrl: user.avatarUrl,
customAttributes: user.customAttributes
);
}

final client =
ChatwootClient._(chatwootParams, callbacks: callbacks, user: user);
ChatwootClient._(
chatwootParams,
callbacks: callbacks,
user: chatUser
);

client._init();

Expand Down
23 changes: 20 additions & 3 deletions lib/chatwoot_parameters.dart
Original file line number Diff line number Diff line change
@@ -1,25 +1,42 @@
import 'dart:convert';

import 'package:equatable/equatable.dart';
import 'package:crypto/crypto.dart';

class ChatwootParameters extends Equatable {
final bool isPersistenceEnabled;
final String baseUrl;
final String clientInstanceKey;
final String inboxIdentifier;
final String? userIdentifier;
final String? userIdentityValidationKey;

ChatwootParameters(
{required this.isPersistenceEnabled,
required this.baseUrl,
required this.inboxIdentifier,
required this.clientInstanceKey,
this.userIdentifier});
this.userIdentityValidationKey});


String generateHmacHash(String key, String userIdentifier) {
// Convert the key and message to bytes
final keyBytes = utf8.encode(key);
final messageBytes = utf8.encode(userIdentifier);

// Create the HMAC using SHA256
final hmac = Hmac(sha256, keyBytes);

final digest = hmac.convert(messageBytes);

return digest.toString();
}

@override
List<Object?> get props => [
isPersistenceEnabled,
baseUrl,
clientInstanceKey,
inboxIdentifier,
userIdentifier
userIdentityValidationKey
];
}
4 changes: 3 additions & 1 deletion lib/data/remote/service/chatwoot_client_api_interceptor.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:chatwoot_sdk/chatwoot_sdk.dart';
import 'package:chatwoot_sdk/data/local/entity/chatwoot_contact.dart';
import 'package:chatwoot_sdk/data/local/entity/chatwoot_conversation.dart';
import 'package:chatwoot_sdk/data/local/local_storage.dart';
Expand Down Expand Up @@ -28,14 +29,15 @@ class ChatwootClientApiInterceptor extends Interceptor {
RequestOptions options, RequestInterceptorHandler handler) async {
await requestLock.synchronized(() async {
RequestOptions newOptions = options;
ChatwootUser? user = _localStorage.userDao.getUser();
ChatwootContact? contact = _localStorage.contactDao.getContact();
ChatwootConversation? conversation =
_localStorage.conversationDao.getConversation();

if (contact == null) {
// create new contact from user if no token found
contact = await _authService.createNewContact(
_inboxIdentifier, _localStorage.userDao.getUser());
_inboxIdentifier, user);
conversation = await _authService.createNewConversation(
_inboxIdentifier, contact.contactIdentifier!);
await _localStorage.conversationDao.saveConversation(conversation);
Expand Down
9 changes: 9 additions & 0 deletions lib/ui/chatwoot_chat_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class ChatwootChatDialog extends StatefulWidget {
BuildContext context, {
required String baseUrl,
required String inboxIdentifier,
String? userIdentityValidationKey,
bool enablePersistence = true,
required String title,
ChatwootUser? user,
Expand All @@ -32,6 +33,7 @@ class ChatwootChatDialog extends StatefulWidget {
return ChatwootChatDialog(
baseUrl: baseUrl,
inboxIdentifier: inboxIdentifier,
userIdentityValidationKey: userIdentityValidationKey,
title: title,
user: user,
enablePersistence: enablePersistence,
Expand All @@ -55,6 +57,12 @@ class ChatwootChatDialog extends StatefulWidget {
/// For more details see https://www.chatwoot.com/docs/product/channels/api/client-apis
final String inboxIdentifier;


///Key used to generate user identifier hash
///
/// For more details see https://www.chatwoot.com/docs/product/channels/api/client-apis
final String? userIdentityValidationKey;

/// Enables persistence of chatwoot client instance's contact, conversation and messages to disk
/// for convenience.
///
Expand Down Expand Up @@ -94,6 +102,7 @@ class ChatwootChatDialog extends StatefulWidget {
Key? key,
required this.baseUrl,
required this.inboxIdentifier,
this.userIdentityValidationKey,
this.enablePersistence = true,
required this.title,
this.user,
Expand Down
8 changes: 8 additions & 0 deletions lib/ui/chatwoot_chat_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ class ChatwootChat extends StatefulWidget {
/// For more details see https://www.chatwoot.com/docs/product/channels/api/client-apis
final String inboxIdentifier;


///Key used to generate user identifier hash
///
/// For more details see https://www.chatwoot.com/docs/product/channels/api/client-apis
final String? userIdentityValidationKey;

/// Enables persistence of chatwoot client instance's contact, conversation and messages to disk
/// for convenience.
///
Expand Down Expand Up @@ -145,6 +151,7 @@ class ChatwootChat extends StatefulWidget {
{Key? key,
required this.baseUrl,
required this.inboxIdentifier,
this.userIdentityValidationKey,
this.enablePersistence = true,
this.user,
this.appBar,
Expand Down Expand Up @@ -334,6 +341,7 @@ class _ChatwootChatState extends State<ChatwootChat> {
ChatwootClient.create(
baseUrl: widget.baseUrl,
inboxIdentifier: widget.inboxIdentifier,
userIdentityValidationKey: widget.userIdentityValidationKey,
user: widget.user,
enablePersistence: widget.enablePersistence,
callbacks: chatwootCallbacks)
Expand Down
2 changes: 1 addition & 1 deletion pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ packages:
source: hosted
version: "3.1.2"
crypto:
dependency: transitive
dependency: "direct main"
description:
name: crypto
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ dependencies:
cached_network_image: ^3.4.0
flutter_markdown: ^0.7.4+2
webview_flutter: ^4.10.0
crypto: ^3.0.6

dev_dependencies:
flutter_test:
Expand Down

0 comments on commit 3d2de1c

Please sign in to comment.