diff --git a/lib/types/identity-service-types.js b/lib/types/identity-service-types.js
--- a/lib/types/identity-service-types.js
+++ b/lib/types/identity-service-types.js
@@ -125,6 +125,7 @@
     siweMessage: string,
     siweSignature: string,
   ) => Promise<IdentityAuthResult>;
+  +publishWebPrekeys?: (prekeys: SignedPrekeys) => Promise<void>;
 }
 
 export type IdentityServiceAuthLayer = {
diff --git a/web/grpc/identity-service-client-wrapper.js b/web/grpc/identity-service-client-wrapper.js
--- a/web/grpc/identity-service-client-wrapper.js
+++ b/web/grpc/identity-service-client-wrapper.js
@@ -3,7 +3,10 @@
 import { Login } from '@commapp/opaque-ke-wasm';
 
 import identityServiceConfig from 'lib/facts/identity-service.js';
-import type { OneTimeKeysResultValues } from 'lib/types/crypto-types.js';
+import type {
+  OneTimeKeysResultValues,
+  SignedPrekeys,
+} from 'lib/types/crypto-types.js';
 import {
   type IdentityServiceAuthLayer,
   type IdentityServiceClient,
@@ -407,6 +410,28 @@
     const result = await this.unauthClient.generateNonce(new Empty());
     return result.getNonce();
   };
+
+  publishWebPrekeys: (prekeys: SignedPrekeys) => Promise<void> = async (
+    prekeys: SignedPrekeys,
+  ) => {
+    const client = this.authClient;
+    if (!client) {
+      throw new Error('Identity service client is not initialized');
+    }
+
+    const contentPrekeyUpload = new Prekey();
+    contentPrekeyUpload.setPrekey(prekeys.contentPrekey);
+    contentPrekeyUpload.setPrekeySignature(prekeys.contentPrekeySignature);
+
+    const notifPrekeyUpload = new Prekey();
+    notifPrekeyUpload.setPrekey(prekeys.notifPrekey);
+    notifPrekeyUpload.setPrekeySignature(prekeys.notifPrekeySignature);
+
+    const request = new IdentityAuthStructs.RefreshUserPrekeysRequest();
+    request.setNewContentPrekeys(contentPrekeyUpload);
+    request.setNewNotifPrekeys(notifPrekeyUpload);
+    await client.refreshUserPrekeys(request);
+  };
 }
 
 function authDeviceKeyUpload(