@@ -12,12 +12,11 @@ import { hashDelegation, decodeDelegations } from '@metamask/delegation-core';
1212import type { Hex } from '@metamask/delegation-core' ;
1313import type {
1414 UserStorageGenericPathWithFeatureAndKey ,
15- JwtBearerAuth ,
1615 UserStorage ,
1716} from '@metamask/profile-sync-controller/sdk' ;
1817import {
18+ InternalError ,
1919 InvalidInputError ,
20- LimitExceededError ,
2120 ParseError ,
2221 UnsupportedMethodError ,
2322} from '@metamask/snaps-sdk' ;
@@ -30,9 +29,6 @@ export type RevocationMetadata = {
3029 recordedAt : number ;
3130} ;
3231
33- // Constants for validation
34- const MAX_STORAGE_SIZE_BYTES = 400 * 1024 ; // 400kb limit as documented
35-
3632// Zod schema for runtime validation of StoredGrantedPermission
3733const zStoredGrantedPermission = z . object ( {
3834 permissionResponse : zPermissionResponse ,
@@ -83,10 +79,9 @@ function safeDeserializeStoredGrantedPermission(
8379}
8480
8581/**
86- * Safely serializes a StoredGrantedPermission object with size validation .
82+ * Safely serializes a StoredGrantedPermission object.
8783 * @param permission - The permission object to serialize.
8884 * @returns The JSON string.
89- * @throws LimitExceededError if size limit exceeded.
9085 */
9186function safeSerializeStoredGrantedPermission (
9287 permission : StoredGrantedPermission ,
@@ -98,19 +93,12 @@ function safeSerializeStoredGrantedPermission(
9893 // Serialize to JSON
9994 const jsonString = JSON . stringify ( validated ) ;
10095
101- // Check size limit
102- const sizeBytes = new TextEncoder ( ) . encode ( jsonString ) . length ;
103- if ( sizeBytes > MAX_STORAGE_SIZE_BYTES ) {
104- throw new LimitExceededError (
105- `Permission data exceeds size limit: ${ sizeBytes } bytes > ${ MAX_STORAGE_SIZE_BYTES } bytes` ,
106- ) ;
107- }
108-
10996 return jsonString ;
11097 } catch ( error ) {
11198 if ( error instanceof z . ZodError ) {
11299 throw new InvalidInputError ( extractZodError ( error . errors ) ) ;
113100 }
101+
114102 throw error ;
115103 }
116104}
@@ -153,7 +141,6 @@ export function generateObjectKey(permissionContext: Hex): Hex {
153141}
154142
155143export type ProfileSyncManagerConfig = {
156- auth : JwtBearerAuth ;
157144 userStorage : UserStorage ;
158145 isFeatureEnabled : boolean ;
159146 snapsMetricsService ?: SnapsMetricsService ;
@@ -168,7 +155,7 @@ export function createProfileSyncManager(
168155 config : ProfileSyncManagerConfig ,
169156) : ProfileSyncManager {
170157 const FEATURE = 'gator_7715_permissions' ;
171- const { auth , userStorage, isFeatureEnabled, snapsMetricsService } = config ;
158+ const { userStorage, isFeatureEnabled, snapsMetricsService } = config ;
172159 const unConfiguredProfileSyncManager = {
173160 getAllGrantedPermissions : async ( ) : Promise < StoredGrantedPermission [ ] > => {
174161 logger . debug ( 'unConfiguredProfileSyncManager.getAllGrantedPermissions()' ) ;
@@ -201,19 +188,6 @@ export function createProfileSyncManager(
201188 } ,
202189 } ;
203190
204- /**
205- * Authenticates the user with profile sync.
206- *
207- */
208- async function authenticate ( ) : Promise < void > {
209- try {
210- await auth . getAccessToken ( ) ;
211- } catch ( error ) {
212- logger . error ( 'Error fetching access token:' , error ) ;
213- throw error ;
214- }
215- }
216-
217191 /**
218192 * Retrieve all granted permission items under the "7715_permissions" feature will result in GET /api/v1/userstorage/7715_permissions
219193 * VALUES: decrypted("JSONstringifyPermission1", storage_key), decrypted("JSONstringifyPermission2", storage_key).
@@ -223,8 +197,6 @@ export function createProfileSyncManager(
223197 StoredGrantedPermission [ ]
224198 > {
225199 try {
226- await authenticate ( ) ;
227-
228200 const items = await userStorage . getAllFeatureItems ( FEATURE ) ;
229201 if ( ! items ) {
230202 await snapsMetricsService ?. trackProfileSync ( {
@@ -271,8 +243,6 @@ export function createProfileSyncManager(
271243 permissionContext : Hex ,
272244 ) : Promise < StoredGrantedPermission | null > {
273245 try {
274- await authenticate ( ) ;
275-
276246 const path : UserStorageGenericPathWithFeatureAndKey = `${ FEATURE } .${ generateObjectKey ( permissionContext ) } ` ;
277247 const permission = await userStorage . getItem ( path ) ;
278248
@@ -287,6 +257,36 @@ export function createProfileSyncManager(
287257 }
288258 }
289259
260+ /**
261+ * Validates that the stored permission value for the given key matches the expected value.
262+ *
263+ * Retrieves the item from user storage using the provided key and compares it to the expected value.
264+ * Throws an InternalError if the item does not exist or if the stored value does not match the expected value.
265+ *
266+ * @param key - The key for the item in user storage.
267+ * @param expectedValue - The expected serialized value to compare against the retrieved value.
268+ * @throws {InternalError } If the stored item is missing or does not match the expected value.
269+ */
270+ async function validatePermissionStored (
271+ key : string ,
272+ expectedValue : string ,
273+ ) : Promise < void > {
274+ const path : UserStorageGenericPathWithFeatureAndKey = `${ FEATURE } .${ key } ` ;
275+ const retrievedValue = await userStorage . getItem ( path ) ;
276+
277+ if ( ! retrievedValue ) {
278+ throw new InternalError (
279+ `Unable to retrieve stored item with key: ${ key } ` ,
280+ ) ;
281+ }
282+
283+ if ( expectedValue !== retrievedValue ) {
284+ throw new InternalError (
285+ `Retrieved stored item with key: ${ key } but does not match expected value` ,
286+ ) ;
287+ }
288+ }
289+
290290 /**
291291 * Store the granted permission in profile sync.
292292 *
@@ -300,27 +300,44 @@ export function createProfileSyncManager(
300300 async function storeGrantedPermission (
301301 storedGrantedPermission : StoredGrantedPermission ,
302302 ) : Promise < void > {
303+ const performanceData = {
304+ permissionCount : 1 ,
305+ permissionsStoredElapsedMs : - 1 ,
306+ storedPermissionsValidatedElapsedMs : - 1 ,
307+ } ;
303308 try {
304- await authenticate ( ) ;
305-
309+ const startTime = Date . now ( ) ;
306310 // Validate and serialize with size check
307311 const serializedPermission = safeSerializeStoredGrantedPermission (
308312 storedGrantedPermission ,
309313 ) ;
310314
311- const path : UserStorageGenericPathWithFeatureAndKey = `${ FEATURE } .${ generateObjectKey ( storedGrantedPermission . permissionResponse . context ) } ` ;
315+ const key = generateObjectKey (
316+ storedGrantedPermission . permissionResponse . context ,
317+ ) ;
318+
319+ const path : UserStorageGenericPathWithFeatureAndKey = `${ FEATURE } .${ key } ` ;
312320 await userStorage . setItem ( path , serializedPermission ) ;
313321
322+ performanceData . permissionsStoredElapsedMs = Date . now ( ) - startTime ;
323+
324+ await validatePermissionStored ( key , serializedPermission ) ;
325+
326+ performanceData . storedPermissionsValidatedElapsedMs =
327+ Date . now ( ) - startTime ;
328+
314329 await snapsMetricsService ?. trackProfileSync ( {
315330 operation : 'store' ,
316331 success : true ,
332+ performanceData,
317333 } ) ;
318334 } catch ( error ) {
319335 logger . error ( 'Error storing granted permission' ) ;
320336 await snapsMetricsService ?. trackProfileSync ( {
321337 operation : 'store' ,
322338 success : false ,
323339 errorMessage : ( error as Error ) . message ,
340+ performanceData,
324341 } ) ;
325342 throw error ;
326343 }
@@ -339,23 +356,25 @@ export function createProfileSyncManager(
339356 async function storeGrantedPermissionBatch (
340357 storedGrantedPermissions : StoredGrantedPermission [ ] ,
341358 ) : Promise < void > {
342- try {
343- await authenticate ( ) ;
359+ const performanceData = {
360+ permissionCount : storedGrantedPermissions . length ,
361+ permissionsStoredElapsedMs : - 1 ,
362+ storedPermissionsValidatedElapsedMs : - 1 ,
363+ } ;
344364
365+ try {
366+ const startTime = Date . now ( ) ;
345367 // Validate and serialize all permissions with size checks
346368 const validatedItems : [ string , string ] [ ] = [ ] ;
347369
348370 for ( const permission of storedGrantedPermissions ) {
349- try {
350- const serializedPermission =
351- safeSerializeStoredGrantedPermission ( permission ) ;
352- validatedItems . push ( [
353- generateObjectKey ( permission . permissionResponse . context ) , // key
354- serializedPermission , // value
355- ] ) ;
356- } catch {
357- logger . warn ( 'Skipping invalid permission in batch' ) ;
358- }
371+ const serializedPermission =
372+ safeSerializeStoredGrantedPermission ( permission ) ;
373+
374+ validatedItems . push ( [
375+ generateObjectKey ( permission . permissionResponse . context ) , // key
376+ serializedPermission , // value
377+ ] ) ;
359378 }
360379
361380 if ( validatedItems . length === 0 ) {
@@ -366,16 +385,30 @@ export function createProfileSyncManager(
366385
367386 await userStorage . batchSetItems ( FEATURE , validatedItems ) ;
368387
388+ performanceData . permissionsStoredElapsedMs = Date . now ( ) - startTime ;
389+
390+ // ensure that all items were stored correctly
391+ const fetchedItems = validatedItems . map ( async ( [ key , value ] ) =>
392+ validatePermissionStored ( key , value ) ,
393+ ) ;
394+
395+ await Promise . all ( fetchedItems ) ;
396+
397+ performanceData . storedPermissionsValidatedElapsedMs =
398+ Date . now ( ) - startTime ;
399+
369400 await snapsMetricsService ?. trackProfileSync ( {
370401 operation : 'batch_store' ,
371402 success : true ,
403+ performanceData,
372404 } ) ;
373405 } catch ( error ) {
374406 logger . error ( 'Error storing granted permission batch' ) ;
375407 await snapsMetricsService ?. trackProfileSync ( {
376408 operation : 'batch_store' ,
377409 success : false ,
378410 errorMessage : ( error as Error ) . message ,
411+ performanceData,
379412 } ) ;
380413 throw error ;
381414 }
@@ -411,8 +444,6 @@ export function createProfileSyncManager(
411444 revocationMetadata,
412445 } ) ;
413446
414- await authenticate ( ) ;
415-
416447 const updatedPermission : StoredGrantedPermission = {
417448 ...existingPermission ,
418449 revocationMetadata,
0 commit comments