Skip to content
Open
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
c10189d
feat: added authenticated user storage
dovydas55 Mar 20, 2026
16163c3
feat: updated changelog
dovydas55 Mar 20, 2026
38b17aa
feat: updated changelog
dovydas55 Mar 20, 2026
2af31ef
feat: fixing linting errors
dovydas55 Mar 20, 2026
782b5ce
feat: fixing linting errors
dovydas55 Mar 20, 2026
27911b3
Merge branch 'main' into MCA-83
dovydas55 Mar 20, 2026
91701fc
feat: updated API contract
dovydas55 Mar 20, 2026
eedd6cc
Merge branch 'MCA-83' of github.com:MetaMask/core into MCA-83
dovydas55 Mar 20, 2026
ce5671d
feat: cleaning up
dovydas55 Mar 23, 2026
cccf519
Merge branch 'main' of github.com:MetaMask/core into MCA-83
dovydas55 Mar 24, 2026
e907cac
feat: added new package authenticated user storage
dovydas55 Mar 24, 2026
f79f384
feat: added missing ASU types
dovydas55 Mar 24, 2026
55f7144
feat: added auth-engineers team
dovydas55 Mar 24, 2026
235b0e5
feat: fixing tests
dovydas55 Mar 24, 2026
0f2599a
feat: fixing tests
dovydas55 Mar 24, 2026
b7918e5
feat: fixing tests
dovydas55 Mar 24, 2026
72e9c2a
feat: fixing tests
dovydas55 Mar 24, 2026
acf46c4
Merge branch 'main' of github.com:MetaMask/core into MCA-83
dovydas55 Mar 24, 2026
3dbc9af
feat: fixing tests
dovydas55 Mar 24, 2026
d534083
Merge branch 'main' of github.com:MetaMask/core into MCA-83
dovydas55 Mar 26, 2026
f2dd2d3
feat: added simple README
dovydas55 Mar 26, 2026
803f104
feat: added simple README
dovydas55 Mar 26, 2026
d415546
feat: added simple README
dovydas55 Mar 26, 2026
00707c6
feat: fixing changelog
dovydas55 Mar 26, 2026
3800ca8
feat: fixing linter
dovydas55 Mar 26, 2026
4d0d255
feat: fixing linter
dovydas55 Mar 26, 2026
c70f646
feat: fixing linter
dovydas55 Mar 26, 2026
d81b087
Merge branch 'main' into MCA-83
dovydas55 Mar 27, 2026
d319cb3
feat: updating docs
dovydas55 Mar 27, 2026
35abfa2
Merge branch 'main' into MCA-83
dovydas55 Mar 30, 2026
a519017
feat: pull main
dovydas55 Apr 10, 2026
ce45739
Merge branch 'MCA-83' of github.com:MetaMask/core into MCA-83
dovydas55 Apr 10, 2026
380d4bb
feat: refactor
dovydas55 Apr 10, 2026
ec5c1af
feat: refactor
dovydas55 Apr 10, 2026
ff3701f
feat: refactor
dovydas55 Apr 10, 2026
71771b0
feat: refactor
dovydas55 Apr 10, 2026
56099a2
feat: refactor
dovydas55 Apr 10, 2026
fa78ff8
feat: refactor
dovydas55 Apr 10, 2026
4a42cb3
Merge branch 'main' into MCA-83
dovydas55 Apr 10, 2026
2a51ee0
feat: fixing build
dovydas55 Apr 10, 2026
c3b4c47
Merge branch 'MCA-83' of github.com:MetaMask/core into MCA-83
dovydas55 Apr 10, 2026
3957705
feat: fixing build
dovydas55 Apr 10, 2026
ad10d98
feat: fixing build
dovydas55 Apr 10, 2026
2d2812d
feat: code review
dovydas55 Apr 10, 2026
7f9b2f7
feat: code review
dovydas55 Apr 10, 2026
4be6409
feat: code review
dovydas55 Apr 10, 2026
b010631
feat: code review
dovydas55 Apr 10, 2026
d2db432
feat: code review
dovydas55 Apr 10, 2026
2339810
feat: code review
dovydas55 Apr 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
/packages/profile-sync-controller @MetaMask/accounts-engineers
/packages/money-account-controller @MetaMask/accounts-engineers

## Auth Team
/packages/authenticated-user-storage @MetaMask/auth-engineers

## Assets Team
/packages/assets-controllers @MetaMask/metamask-assets
/packages/network-enablement-controller @MetaMask/metamask-assets
Expand Down Expand Up @@ -180,6 +183,8 @@
/packages/phishing-controller/CHANGELOG.md @MetaMask/product-safety @MetaMask/core-platform
/packages/ramps-controller/package.json @MetaMask/money-movement @MetaMask/core-platform
/packages/ramps-controller/CHANGELOG.md @MetaMask/money-movement @MetaMask/core-platform
/packages/authenticated-user-storage/package.json @MetaMask/auth-engineers @MetaMask/core-platform
/packages/authenticated-user-storage/CHANGELOG.md @MetaMask/auth-engineers @MetaMask/core-platform
/packages/profile-sync-controller/package.json @MetaMask/accounts-engineers @MetaMask/core-platform
/packages/profile-sync-controller/CHANGELOG.md @MetaMask/accounts-engineers @MetaMask/core-platform
/packages/selected-network-controller/package.json @MetaMask/wallet-integrations @MetaMask/core-platform
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Each package in this repository has its own README where you can find installati
- [`@metamask/approval-controller`](packages/approval-controller)
- [`@metamask/assets-controller`](packages/assets-controller)
- [`@metamask/assets-controllers`](packages/assets-controllers)
- [`@metamask/authenticated-user-storage`](packages/authenticated-user-storage)
- [`@metamask/base-controller`](packages/base-controller)
- [`@metamask/base-data-service`](packages/base-data-service)
- [`@metamask/bridge-controller`](packages/bridge-controller)
Expand Down Expand Up @@ -117,6 +118,7 @@ linkStyle default opacity:0.5
approval_controller(["@metamask/approval-controller"]);
assets_controller(["@metamask/assets-controller"]);
assets_controllers(["@metamask/assets-controllers"]);
authenticated_user_storage(["@metamask/authenticated-user-storage"]);
base_controller(["@metamask/base-controller"]);
base_data_service(["@metamask/base-data-service"]);
bridge_controller(["@metamask/bridge-controller"]);
Expand Down Expand Up @@ -243,6 +245,9 @@ linkStyle default opacity:0.5
assets_controllers --> profile_sync_controller;
assets_controllers --> storage_service;
assets_controllers --> transaction_controller;
authenticated_user_storage --> base_data_service;
authenticated_user_storage --> controller_utils;
authenticated_user_storage --> messenger;
base_controller --> messenger;
base_controller --> json_rpc_engine;
base_data_service --> controller_utils;
Expand Down
5 changes: 0 additions & 5 deletions eslint-suppressions.json
Original file line number Diff line number Diff line change
Expand Up @@ -2261,11 +2261,6 @@
"count": 1
}
},
"packages/transaction-pay-controller/src/utils/source-amounts.ts": {
"import-x/no-relative-packages": {
"count": 1
}
},
"packages/transaction-pay-controller/src/utils/transaction.ts": {
"no-restricted-syntax": {
"count": 2
Expand Down
15 changes: 15 additions & 0 deletions packages/authenticated-user-storage/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Initial release ([#8260](https://github.com/MetaMask/core/pull/8260))
- `AuthenticatedUserStorageService` class with namespaced domain accessors: `delegations` (list, create, revoke) and `preferences` (getNotifications, putNotifications)

[Unreleased]: https://github.com/MetaMask/core/
20 changes: 20 additions & 0 deletions packages/authenticated-user-storage/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
MIT License

Copyright (c) 2026 MetaMask

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
141 changes: 141 additions & 0 deletions packages/authenticated-user-storage/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# `@metamask/authenticated-user-storage`

A TypeScript SDK for MetaMask's Authenticated User Storage API. Unlike E2EE user-storage, authenticated user storage holds **structured JSON** scoped to the authenticated user. The server can read and validate the contents, which allows other backend services to consume the data (e.g. delegation execution, notification delivery).

The SDK currently supports two domains:

- **Delegations** -- immutable, EIP-712 signed delegation records (list, create, revoke).
- **Notification Preferences** -- mutable per-user notification settings (get, put).

## Installation

`yarn add @metamask/authenticated-user-storage`

or

`npm install @metamask/authenticated-user-storage`

## Usage

### Creating a service

`AuthenticatedUserStorageService` extends `BaseDataService` and requires a messenger, an environment, and an access-token callback:

- **`messenger`** -- a namespaced messenger for registering actions and events.
- **`env`** -- selects the backend environment (`DEV`, `UAT`, or `PRD`).
- **`getAccessToken`** -- an async callback that returns a valid JWT access token for the current user.

```typescript
import { Messenger } from '@metamask/messenger';
import {
AuthenticatedUserStorageService,
Env,
} from '@metamask/authenticated-user-storage';
import type {
AuthenticatedUserStorageMessenger,
AuthenticatedUserStorageActions,
AuthenticatedUserStorageEvents,
} from '@metamask/authenticated-user-storage';

// Create the messenger
const messenger = new Messenger<
'AuthenticatedUserStorage',
AuthenticatedUserStorageActions,
AuthenticatedUserStorageEvents
>({
namespace: 'AuthenticatedUserStorage',
parent: rootMessenger,
});

// Instantiate the service
const service = new AuthenticatedUserStorageService({
messenger,
env: Env.PRD,
getAccessToken: () =>
rootMessenger.call('AuthenticationController:getBearerToken'),
});
```

The `env` option selects the backend environment:

| `Env` value | Server |
| ----------- | ------------------------------------- |
| `Env.DEV` | `user-storage.dev-api.cx.metamask.io` |
| `Env.UAT` | `user-storage.uat-api.cx.metamask.io` |
| `Env.PRD` | `user-storage.api.cx.metamask.io` |

### Calling methods via the messenger

Once instantiated, all service methods are available as messenger actions. This allows any consumer with access to the messenger to call them without needing a direct reference to the service instance:

```typescript
const delegations = await rootMessenger.call(
'AuthenticatedUserStorage:listDelegations',
);
```

### Delegations

Delegations are immutable once stored. They can only be revoked (deleted), not updated.

```typescript
import type { DelegationSubmission } from '@metamask/authenticated-user-storage';

// List all delegations for the authenticated user
const delegations = await service.listDelegations();

// Submit a new signed delegation
const submission: DelegationSubmission = {
signedDelegation: { ... },
metadata: { ... },
};
await service.createDelegation(submission, 'extension');

// Revoke a delegation by its hash
await service.revokeDelegation('0xdae6d1...');
```

### Notification preferences

Preferences are mutable. The first call creates the record; subsequent calls update it.

```typescript
import type { NotificationPreferences } from '@metamask/authenticated-user-storage';

// Retrieve current preferences (returns null if none have been set)
const prefs = await service.getNotificationPreferences();

// Create or update preferences
const updated: NotificationPreferences = {
walletActivity: { ... },
marketing: { ... },
perps: { ... },
socialAI: { ... },
};
await service.putNotificationPreferences(updated, 'extension');
```

## Response validation

All API responses are validated at runtime using [`@metamask/superstruct`](https://github.com/MetaMask/superstruct) schemas before being returned to callers. If the server returns data that doesn't match the expected shape, the SDK throws with details about the structural mismatch rather than silently returning malformed data.

## Error handling

HTTP errors are represented as `HttpError` from `@metamask/controller-utils`. All errors are encouraged to bubble up to the caller. The service policy provided by `BaseDataService` automatically retries transient failures before propagating the error.

```typescript
import { HttpError } from '@metamask/controller-utils';

try {
await service.createDelegation(submission);
} catch (error) {
if (error instanceof HttpError) {
console.error(error.message);
// e.g. "Failed to create delegation: 409"
}
}
```

## Contributing

This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/core#readme).
32 changes: 32 additions & 0 deletions packages/authenticated-user-storage/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* For a detailed explanation regarding each configuration property and type check, visit:
* https://jestjs.io/docs/configuration
*/

const merge = require('deepmerge');
const path = require('path');

const baseConfig = require('../../jest.config.packages');

const displayName = path.basename(__dirname);

module.exports = merge(baseConfig, {
// The display name when running multiple projects
displayName,

// An object that configures minimum threshold enforcement for coverage results
coverageThreshold: {
global: {
branches: 90,
functions: 90,
lines: 90,
statements: 90,
},
},

coveragePathIgnorePatterns: [
...baseConfig.coveragePathIgnorePatterns,
'/tests/',
'index.ts',
],
});
78 changes: 78 additions & 0 deletions packages/authenticated-user-storage/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
{
"name": "@metamask/authenticated-user-storage",
"version": "0.0.0",
"description": "SDK for authenticated (non-encrypted) user storage endpoints",
"keywords": [
"MetaMask",
"Ethereum"
],
"homepage": "https://github.com/MetaMask/core/tree/main/packages/authenticated-user-storage#readme",
"bugs": {
"url": "https://github.com/MetaMask/core/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/MetaMask/core.git"
},
"license": "MIT",
"sideEffects": false,
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
},
"./package.json": "./package.json"
},
"main": "./dist/index.cjs",
"types": "./dist/index.d.cts",
"files": [
"dist/"
],
"scripts": {
"build": "ts-bridge --project tsconfig.build.json --verbose --clean --no-references",
"build:all": "ts-bridge --project tsconfig.build.json --verbose --clean",
"build:docs": "typedoc",
"changelog:update": "../../scripts/update-changelog.sh @metamask/authenticated-user-storage",
"changelog:validate": "../../scripts/validate-changelog.sh @metamask/authenticated-user-storage",
"messenger-action-types:check": "tsx ../../packages/messenger-cli/src/cli.ts --check",
"messenger-action-types:generate": "tsx ../../packages/messenger-cli/src/cli.ts --generate",
"since-latest-release": "../../scripts/since-latest-release.sh",
"test": "NODE_OPTIONS=--experimental-vm-modules jest --reporters=jest-silent-reporter",
"test:clean": "NODE_OPTIONS=--experimental-vm-modules jest --clearCache",
"test:verbose": "NODE_OPTIONS=--experimental-vm-modules jest --verbose",
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch"
},
"dependencies": {
"@metamask/base-data-service": "^0.1.1",
"@metamask/controller-utils": "^11.20.0",
"@metamask/messenger": "^1.1.1",
"@metamask/superstruct": "^3.1.0",
"@metamask/utils": "^11.9.0"
},
"devDependencies": {
"@metamask/auto-changelog": "^3.4.4",
"@ts-bridge/cli": "^0.6.4",
"@types/jest": "^29.5.14",
"deepmerge": "^4.2.2",
"jest": "^29.7.0",
"nock": "^13.3.1",
"ts-jest": "^29.2.5",
"tsx": "^4.20.5",
"typedoc": "^0.25.13",
"typedoc-plugin-missing-exports": "^2.0.0",
"typescript": "~5.3.3"
},
"engines": {
"node": "^18.18 || >=20"
},
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
}
}
Loading
Loading