Skip to content

Comments

[PB-5714] Add mail API to the sdk#358

Merged
TamaraFinogina merged 17 commits intomasterfrom
add_mail_api
Feb 19, 2026
Merged

[PB-5714] Add mail API to the sdk#358
TamaraFinogina merged 17 commits intomasterfrom
add_mail_api

Conversation

@TamaraFinogina
Copy link
Contributor

This PR adds functions needed for implementing Mail

@TamaraFinogina TamaraFinogina marked this pull request as ready for review January 21, 2026 17:24
@TamaraFinogina TamaraFinogina requested a review from sg-gs January 21, 2026 17:25
clientName: this.appDetails.clientName,
clientVersion: this.appDetails.clientVersion,
token: this.apiSecurity.token,
workspaceToken: this.apiSecurity.workspaceToken,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the workspaceToken or the desktopToken going to be needed for the meet server api?
If not, maybe its better to remove them from here 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, I took this part from drive I think

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope, copied headersWithToken from meet

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe they should be removed from there too haha

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@larryrider Fixed

@larryrider
Copy link
Contributor

If you are going to do a new release, remember to update the package.json version

*/
async decryptEmail(email: HybridEncryptedEmail, keys: EmailKeys): Promise<Email> {
const senderEmail = email.params.sender.email;
const pk = await this.getUserPublicKeys(senderEmail);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is pk and why it contains the publicKeys while other object (keys) contains the private ones? I feel this confusing unless the pk name is clearer

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pk is the public key of the sender. To decrypt the email, we need both the public key of the person who sent the email and the private key of the receiver. I can rename pk to senderPublicKeys

*/
async getPublicKeysOfSeveralUsers(emails: string[]): Promise<UserWithPublicKeys[]> {
const response = await this.client.getWithParams<{ publicKeys: PublicKeysBase64; user: User }[]>(
`${this.apiUrl}/getPublicKeysOfSeveralUsers`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This path is not following REST. Something like /users/keys with a body would be better

*/
async getUserPublicKeys(userEmail: string): Promise<UserWithPublicKeys> {
const response = await this.client.getWithParams<{ publicKeys: PublicKeysBase64; user: User }>(
`${this.apiUrl}/getUserPublicKeys`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

* @returns Server response
*/
async uploadKeystoreToServer(encryptedKeystore: EncryptedKeystore): Promise<void> {
return this.client.post(`${this.apiUrl}/uploadKeystore`, { encryptedKeystore }, this.headers());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here:

POST /keys

Would be better

* @returns Server response
*/
async sendEncryptedEmail(email: HybridEncryptedEmail): Promise<void> {
return this.client.post(`${this.apiUrl}/sendEncryptedEmail`, { email }, this.headers());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

POST /mails

Would be more correct

* @returns Server response
*/
async sendEncryptedEmailToMultipleRecipients(emails: HybridEncryptedEmail[]): Promise<void> {
return this.client.post(`${this.apiUrl}/sendEncryptedEmailToMultipleRecipients`, { emails }, this.headers());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

* @returns Server response
*/
async sendPwdProtectedEmail(email: PwdProtectedEmail): Promise<void> {
return this.client.post(`${this.apiUrl}/sendPwdProtectedEmail`, { email }, this.headers());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

* @returns The encrypted keystore
*/
async downloadKeystoreFromServer(userEmail: string, keystoreType: KeystoreType): Promise<EncryptedKeystore> {
return this.client.getWithParams(`${this.apiUrl}/getKeystore`, { userEmail, keystoreType }, this.headers());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

GET /keys

@TamaraFinogina
Copy link
Contributor Author

@sg-gs I've changed the paths, now they are:

  • uploadKeystoreToServer → /keystore
  • downloadKeystoreFromServer → /user/keystore
  • getUserWithPublicKeys → /user/public-keys
  • getSeveralUsersWithPublicKeys → /users/public-keys
  • sendEncryptedEmail → /email/encrypted
  • sendEncryptedEmailToMultipleRecipients → /emails/encrypted
  • sendPasswordProtectedEmail → /email/password-protected
  1. I've used public-keys instead of keys because there are also private keys, and it might be confusing
  2. The email can be encrypted with Kyber + ECC or protected with a shared password, which results in different encrypted email types (HybridEncryptedEmail and PwdProtectedEmail) - hence 2 different post methods
  3. The email can be encrypted for several recipients, in which case the encrypted emails HybridEncryptedEmail are different for each recipient because recipientEmail and encryptedKey will be different based on who the exact recipient is, the rest of the HybridEncryptedEmail will be the same. I can adjust the type if necessary
 `HybridEncryptedEmail = {
    encryptedKey: HybridEncKey;
    enc: EmailBodyEncrypted;
    recipientEmail: string;
    params: EmailPublicParameters;
    id: string;
    isSubjectEncrypted: boolean;
}

@TamaraFinogina TamaraFinogina requested a review from sg-gs January 22, 2026 17:10
@sg-gs
Copy link
Member

sg-gs commented Jan 23, 2026

@sg-gs I've changed the paths, now they are:

  • uploadKeystoreToServer → /keystore
  • downloadKeystoreFromServer → /user/keystore
  • getUserWithPublicKeys → /user/public-keys
  • getSeveralUsersWithPublicKeys → /users/public-keys
  • sendEncryptedEmail → /email/encrypted
  • sendEncryptedEmailToMultipleRecipients → /emails/encrypted
  • sendPasswordProtectedEmail → /email/password-protected
  1. I've used public-keys instead of keys because there are also private keys, and it might be confusing
  2. The email can be encrypted with Kyber + ECC or protected with a shared password, which results in different encrypted email types (HybridEncryptedEmail and PwdProtectedEmail) - hence 2 different post methods
  3. The email can be encrypted for several recipients, in which case the encrypted emails HybridEncryptedEmail are different for each recipient because recipientEmail and encryptedKey will be different based on who the exact recipient is, the rest of the HybridEncryptedEmail will be the same. I can adjust the type if necessary
 `HybridEncryptedEmail = {
    encryptedKey: HybridEncKey;
    enc: EmailBodyEncrypted;
    recipientEmail: string;
    params: EmailPublicParameters;
    id: string;
    isSubjectEncrypted: boolean;
}

@TamaraFinogina
I see some potential improvements on the paths like entities should be always in plural and the duplication of EPs that should be under the same path. Thus I suggest you the following routes which I am pretty sure @jzunigax2 will almost agree:

  • uploadKeystoreToServer → POST /keystore
  • downloadKeystoreFromServer → GET /keystore
  • getUserWithPublicKeys → GET /users/public-keys?emails=<one_email_or_more>
  • getSeveralUsersWithPublicKeys → idem ⬆️
  • sendEncryptedEmail → POST /emails
  • sendEncryptedEmailToMultipleRecipients → idem ⬆️ (but different body)
  • sendPasswordProtectedEmail → idem ⬆️ (but different body)

Idea: Same action but additional params → same route (generally)

@jzunigax2
Copy link
Contributor

@sg-gs I've changed the paths, now they are:

  • uploadKeystoreToServer → /keystore
  • downloadKeystoreFromServer → /user/keystore
  • getUserWithPublicKeys → /user/public-keys
  • getSeveralUsersWithPublicKeys → /users/public-keys
  • sendEncryptedEmail → /email/encrypted
  • sendEncryptedEmailToMultipleRecipients → /emails/encrypted
  • sendPasswordProtectedEmail → /email/password-protected
  1. I've used public-keys instead of keys because there are also private keys, and it might be confusing
  2. The email can be encrypted with Kyber + ECC or protected with a shared password, which results in different encrypted email types (HybridEncryptedEmail and PwdProtectedEmail) - hence 2 different post methods
  3. The email can be encrypted for several recipients, in which case the encrypted emails HybridEncryptedEmail are different for each recipient because recipientEmail and encryptedKey will be different based on who the exact recipient is, the rest of the HybridEncryptedEmail will be the same. I can adjust the type if necessary
 `HybridEncryptedEmail = {
    encryptedKey: HybridEncKey;
    enc: EmailBodyEncrypted;
    recipientEmail: string;
    params: EmailPublicParameters;
    id: string;
    isSubjectEncrypted: boolean;
}

@TamaraFinogina I see some potential improvements on the paths like entities should be always in plural and the duplication of EPs that should be under the same path. Thus I suggest you the following routes which I am pretty sure @jzunigax2 will almost agree:

* uploadKeystoreToServer → POST /keystore

* downloadKeystoreFromServer → GET /keystore

* getUserWithPublicKeys → GET /users/public-keys?emails=<one_email_or_more>

* getSeveralUsersWithPublicKeys → idem ⬆️

* sendEncryptedEmail → POST /emails

* sendEncryptedEmailToMultipleRecipients → idem ⬆️ (but different body)

* sendPasswordProtectedEmail → idem ⬆️ (but different body)

Idea: Same action but additional params → same route (generally)

yeah agreed, only the get keys requests seem like a POST to me, so we avoid sending the emails in query params as I know there is a max url limit (though I have no clue how big or small this limit is) or avoid possiby exposing recipients in a server request log

@TamaraFinogina
Copy link
Contributor Author

@sg-gs @jzunigax2, functions now are the following:

  • uploadKeystoreToServer → /keystore
  • downloadKeystoreFromServer → /user/keystore
  • getUserWithPublicKeys →/users/public-keys with parameter emails: [userEmail]
  • getSeveralUsersWithPublicKeys → /users/public-keys with parameter emails
  • sendEncryptedEmail → /emails with parameter emails: [encryptedEmail]
  • sendEncryptedEmailToMultipleRecipients → /emails with parameter emails
  • sendPasswordProtectedEmail → /emails with parameter email

All of them are currently POST because getSeveralUsersWithPublicKeys and downloadKeystoreFromServer have user emails/email as parameters. I'm not sure we should expose them as part of the URL

P.S. I'm not sure if sendPasswordProtectedEmail should have emails, email, or something like pwd-email as input

@TamaraFinogina TamaraFinogina requested review from jzunigax2 and removed request for xabg2 February 17, 2026 09:57
Comment on lines +116 to +117
const singleResponse = response[0];
const publicKeys = await base64ToPublicKey(singleResponse.publicKeys);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

singleResponse.publicKeys will throw if the server returns an empty array
Consider adding a guard:
if (!response[0]) throw new Error(No public keys found for ${userEmail});

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, check is added

"uuid": "13.0.0"
"uuid": "13.0.0",
"internxt-crypto": "https://github.com/internxt/crypto/releases/download/v.0.0.12/internxt-crypto-0.0.12.tgz"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to create a task to release this package to npm

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'm preparing the release

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, it uses version 0.0.13 now

]);
});

it('should successfully create and upload a keystore', async () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use when-then

Copy link
Contributor Author

@TamaraFinogina TamaraFinogina Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

something like:

'When a keystore upload is requested, then it should successfully upload the keystore.'?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sg-gs Done

@TamaraFinogina TamaraFinogina requested a review from sg-gs February 19, 2026 14:46
@TamaraFinogina TamaraFinogina merged commit 9d7fea7 into master Feb 19, 2026
1 check passed
@TamaraFinogina TamaraFinogina deleted the add_mail_api branch February 19, 2026 15:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants