N
Novu•14mo ago
Jelle

Enabling HMAC for in-app with C#

Hey guy's im setting up HMAC for in-app and endpoint https://api.novu.co/v1/widgets/session/initialize is giving me a 400 Bad Request "Please provide a valid HMAC hash" and I was wondering if there's some obvious error im making when generating it
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(_novuConfig.ApiKey));
var notificationCenterHash = Convert.ToHexString(hmac.ComputeHash(Encoding.UTF8.GetBytes(subscriberId)));
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(_novuConfig.ApiKey));
var notificationCenterHash = Convert.ToHexString(hmac.ComputeHash(Encoding.UTF8.GetBytes(subscriberId)));
I'm basically using the React example from the docs:
<NovuProvider
subscriberId={user.id}
applicationIdentifier={env.NOVU_APP_ID}
subscriberHash={user?.notificationCenterHash}
>
<PopoverNotificationCenter colorScheme="light">
{({ unseenCount }) => <NotificationBell unseenCount={unseenCount} />}
</PopoverNotificationCenter>
</NovuProvider>
<NovuProvider
subscriberId={user.id}
applicationIdentifier={env.NOVU_APP_ID}
subscriberHash={user?.notificationCenterHash}
>
<PopoverNotificationCenter colorScheme="light">
{({ unseenCount }) => <NotificationBell unseenCount={unseenCount} />}
</PopoverNotificationCenter>
</NovuProvider>
21 Replies
Jelle
JelleOP•14mo ago
The novu api key, app id, and the subscriber id seems to be correct
Pawan Jain
Pawan Jain•14mo ago
Unfortunately, I could help in nodejs 😅 Not sure if your above .net code is correct
Jelle
JelleOP•14mo ago
So I fiddled around with it and turns out that it was happening because the Convert.ToHexString generates it in uppercase, converting it to lowercase fixes it
string ToHex(byte[] hash)
{
var hexBuilder = new StringBuilder(hash.Length * 2);
foreach (var b in hash)
{
// The 'x2' format generates two lowercased hex characters per entry
hexBuilder.Append($"{b:x2}");
}

return hexBuilder.ToString();
}
string ToHex(byte[] hash)
{
var hexBuilder = new StringBuilder(hash.Length * 2);
foreach (var b in hash)
{
// The 'x2' format generates two lowercased hex characters per entry
hexBuilder.Append($"{b:x2}");
}

return hexBuilder.ToString();
}
May be worth adding to the docs that it should be a lowercased hex hash @Pawan Jain
Novu_Bot
Novu_Bot•14mo ago
@Jelle, you just advanced to level 3!
dhruv.barot
dhruv.barot•12mo ago
@Jelle : Are your issues fixed? I am facing the same issue, I did the lowecased hex hash.
Jelle
JelleOP•12mo ago
My last message got it working for me
dhruv.barot
dhruv.barot•12mo ago
Here is my implimentation, Do you see any problem?
No description
Novu_Bot
Novu_Bot•12mo ago
@dhruv.barot, you just advanced to level 1!
Jelle
JelleOP•12mo ago
This is my implementation, ComputeHash(string) is called with the subscriberId
/// <summary>
/// Used to generate HMAC encrypted userIds for using the Novu Notification Center API.
/// See: <a href="https://docs.novu.co/notification-center/client/react/get-started#enabling-hmac-encryption">Novu Docs</a> for more info
/// </summary>
public sealed class NovuNotificationCenterHashProvider : INotificationCenterHashProvider, IDisposable
{
private readonly HMACSHA256 _hmac;

/// <param name="key">The secret key for HMACSHA256 encryption. The key can be any length. However, the recommended size is 64 bytes. If the key is more than 64 bytes long, it is hashed (using SHA-256) to derive a 64-byte key. If it is less than 64 bytes long, it is padded to 64 bytes.</param>
/// <exception cref="ArgumentNullException">Thrown if <see cref="key"/> is null or empty</exception>
public NovuNotificationCenterHashProvider(string key)
{
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentNullException(nameof(key));

_hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key));
}

public NovuNotificationCenterHashProvider(IOptions<NovuConfiguration> options) : this(options.Value.ApiKey!)
{
}

public string ComputeHash(string source)
{
var hash = _hmac.ComputeHash(Encoding.UTF8.GetBytes(source));

return ToHex(hash);
}

private static string ToHex(IReadOnlyCollection<byte> hash)
{
var hexBuilder = new StringBuilder(hash.Count * 2);
foreach (var b in hash)
{
// The 'x2' format generates two lowercased hex characters per entry
hexBuilder.Append($"{b:x2}");
}

return hexBuilder.ToString();
}

public void Dispose()
{
_hmac.Dispose();
}
}
/// <summary>
/// Used to generate HMAC encrypted userIds for using the Novu Notification Center API.
/// See: <a href="https://docs.novu.co/notification-center/client/react/get-started#enabling-hmac-encryption">Novu Docs</a> for more info
/// </summary>
public sealed class NovuNotificationCenterHashProvider : INotificationCenterHashProvider, IDisposable
{
private readonly HMACSHA256 _hmac;

/// <param name="key">The secret key for HMACSHA256 encryption. The key can be any length. However, the recommended size is 64 bytes. If the key is more than 64 bytes long, it is hashed (using SHA-256) to derive a 64-byte key. If it is less than 64 bytes long, it is padded to 64 bytes.</param>
/// <exception cref="ArgumentNullException">Thrown if <see cref="key"/> is null or empty</exception>
public NovuNotificationCenterHashProvider(string key)
{
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentNullException(nameof(key));

_hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key));
}

public NovuNotificationCenterHashProvider(IOptions<NovuConfiguration> options) : this(options.Value.ApiKey!)
{
}

public string ComputeHash(string source)
{
var hash = _hmac.ComputeHash(Encoding.UTF8.GetBytes(source));

return ToHex(hash);
}

private static string ToHex(IReadOnlyCollection<byte> hash)
{
var hexBuilder = new StringBuilder(hash.Count * 2);
foreach (var b in hash)
{
// The 'x2' format generates two lowercased hex characters per entry
hexBuilder.Append($"{b:x2}");
}

return hexBuilder.ToString();
}

public void Dispose()
{
_hmac.Dispose();
}
}
I don't really see anything wrong in your code though, looks the same Let me check if it still works in our app lmao
dhruv.barot
dhruv.barot•12mo ago
Thanks, Kindly check
Jelle
JelleOP•12mo ago
Yep it does Are you sure you're using the correct values everywhere? And that it's sent correctly to the novu api
dhruv.barot
dhruv.barot•12mo ago
curl --location 'https://api.novu.co/v1/widgets/session/initialize' \ --header 'Authorization: ApiKey 17e6db6160cXXXXXX' \ --header 'Content-Type: application/json' \ --data '{ "applicationIdentifier": "F_5Gu0excnDH", "subscriberId": "cbe4b020beed7774dcb298f87186e4e9bd440164b2ae5538c53543dee5c4ad1e" }' Do you think the curl is correct? Yes, recheck all values and it's correct
Jelle
JelleOP•12mo ago
Youre not sending the hmac hash though
dhruv.barot
dhruv.barot•12mo ago
Can you please send curl or any implimentation details for initializing the session endpoint?
Jelle
JelleOP•12mo ago
Im pretty sure you just POST this
{
"applicationIdentifier": "<application_identifier>",
"subscriberId": "<subscriberId>",
"hmacHash": "<hash>"
}
{
"applicationIdentifier": "<application_identifier>",
"subscriberId": "<subscriberId>",
"hmacHash": "<hash>"
}
You're not supposed to send your api key over from the client
dhruv.barot
dhruv.barot•12mo ago
Great thanks: It's working @Pawan Jain : I am happy to add documentation for session initialize API.
Pawan Jain
Pawan Jain•12mo ago
Sure @dhruv.barot feel free to ping us here in this thread or @unicodeveloper if you need any help 🙂
nayr
nayr•11mo ago
Hello! Could I check how do you all pass the generated hmac to the client in terms of single page application like react?
empe
empe•11mo ago
To pass the generated HMAC to a client in a single-page application like React, you can generate an HMAC encrypted subscriberId on your backend, and then pass it along to your React client-side application. This approach ensures that the subscriberId is securely encrypted using your secret API key, protecting against impersonation by malicious actors. Here is an example of how the HMAC can be generated on the backend:
import { createHmac } from "crypto";
const hmacHash = createHmac("sha256", process.env.NOVU_API_KEY)
.update(subscriberId)
.digest("hex");
import { createHmac } from "crypto";
const hmacHash = createHmac("sha256", process.env.NOVU_API_KEY)
.update(subscriberId)
.digest("hex");
After creating the HMAC, you would pass the subscriberId and the generated subscriberHash to the client-side application and include them when initializing the <NovuProvider> component:
<NovuProvider
subscriberId={"SUBSCRIBER_ID_PLAIN_VALUE"}
subscriberHash={"SUBSCRIBER_ID_HASH_VALUE"}
applicationIdentifier={"APPLICATION_IDENTIFIER"}
>
<NotificationCenter colorScheme="dark" />
</NovuProvider>
<NovuProvider
subscriberId={"SUBSCRIBER_ID_PLAIN_VALUE"}
subscriberHash={"SUBSCRIBER_ID_HASH_VALUE"}
applicationIdentifier={"APPLICATION_IDENTIFIER"}
>
<NotificationCenter colorScheme="dark" />
</NovuProvider>
The subscription to notifications will not be operational if you have enabled HMAC encryption and then fail to provide both subscriberId and subscriberHash to the <NovuProvider> component. Ensure that you enable HMAC encryption in the admin panel settings of your Novu account. The actual HMAC generation should occur on a backend server where the API key can remain hidden. Only the resulting HMAC hash should be sent to the client.
No description
Pawan Jain
Pawan Jain•11mo ago
@Jelle Let me know if you need more help in this issue.
Jelle
JelleOP•11mo ago
I fixed it with this
Want results from more Discord servers?
Add your server