Skip to content

Conversation

@huggingbot
Copy link
Member

Explanation

The metadata service now supports a dataType column for categorizing secret data (PrimarySrp, ImportedSrp, ImportedPrivateKey). This enables clients to distinguish between different types of backed-up secrets

Changes include:

  • Add dataType parameter to insert operations
  • Add updateSecretDataItem and batchUpdateSecretDataItems for updating existing items
  • Update fetchAllSecretData to return storage metadata (itemId, dataType) alongside secret data
  • Refactor SecretMetadata to separate local metadata from storage-level metadata

References

https://consensyssoftware.atlassian.net/browse/SL-350

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

- Add dataType parameter to createToprfKeyAndBackupSeedPhrase and addNewSecretData
- Add updateSecretDataItem and batchUpdateSecretDataItems methods
- Update fetchAllSecretData to return SecretDataItemWithMetadata[]
/**
* The client-assigned data type classification.
*/
dataType?: EncAccountDataType;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need this extra type SecretMetadata already has it has type

Copy link
Member Author

Choose a reason for hiding this comment

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

type is in encrypted data, which the server can't access to
data_type is a server-side column
the server needs to know the type of key for the deletion api: we're preventing clients from deleting the default srp

/**
* The server-assigned item ID for this row.
*/
itemId?: string;
Copy link
Contributor

Choose a reason for hiding this comment

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

Should add to the SecretMetadata instead of creating another wrapping types

Copy link
Member Author

@huggingbot huggingbot Dec 2, 2025

Choose a reason for hiding this comment

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

itemId is not part of the secret data, hence it shouldn't belong inside SecretMetadata. Only data, timestamp, type and version are part of SecretMetadata

/**
* A secret data item with storage-level metadata from the metadata store.
*/
export type SecretDataItemWithMetadata<
Copy link
Contributor

Choose a reason for hiding this comment

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

Please add the required metadata to the SecretMetadata class instead of creating another type defs.

The purpose the metadata class is to store and do things with extra non-secret data.

Copy link
Member Author

@huggingbot huggingbot Dec 2, 2025

Choose a reason for hiding this comment

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

itemId and dataType are not part of the secret data. They belong outside of it, hence the wrapper type SecretDataItemWithMetadata is introduced.

The purpose the metadata class is to store and do things with extra non-secret data.

by the metadata class, did u mean the SecretMetadata class?

Copy link
Member Author

Choose a reason for hiding this comment

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

moved itemId and dataType here

export { SecretMetadata } from './SecretMetadata';
export { RecoveryError } from './errors';

export { EncAccountDataType } from '@metamask/toprf-secure-backup';
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure why we export this, but this should not be determined from the users (ext or mobile).

Copy link
Member Author

@huggingbot huggingbot Dec 2, 2025

Choose a reason for hiding this comment

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

it's exported so clients can use it in createToprfKeyAndBackupSeedPhrase, addNewSecretData, updateSecretDataItem and batchUpdateSecretDataItems. The clients do determine the type of secret, e.g. in addNewSecretData, there's a type param of type SecretType where the client can decide if it's a mnemonic or privateKey

password: string,
seedPhrase: Uint8Array,
keyringId: string,
dataType: EncAccountDataType = EncAccountDataType.PrimarySrp,
Copy link
Contributor

Choose a reason for hiding this comment

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

createToprfKeyAndBackupSeedPhrase only used for new users scenario, this param should not be exposed at all and type is always primary.

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed here

type: SecretType,
options?: {
keyringId?: string;
dataType?: EncAccountDataType;
Copy link
Contributor

Choose a reason for hiding this comment

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

we have type field in 2nd param, we don't need new field here.

Copy link
Member Author

@huggingbot huggingbot Dec 2, 2025

Choose a reason for hiding this comment

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

SecretType and EncAccountDataType are slightly different:

  • SecretType only has 2 types: SRP or private key; EncAccountDataType has 3: Primary SRP, imported SRP, imported private key
  • Even if we were to extend SecretType to include a primary SRP, using string values should be avoided to protect user privacy when data is transmitted to the server

we can attempt to migrate SecretType from string to number, making it essentially the same as EncAccountDataType. Though they are essentially different things, and in the future we might want to remove type from the encrypted data since we will use dataType as the source of truth. What do u think?

* @param params.dataType - The data type to set for the item.
* @returns A promise that resolves when the update is complete.
*/
async updateSecretDataItem(params: {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we specify why and when we need to call this? since in the clients, update* is not something we usually do with SRPs

Copy link
Member Author

@huggingbot huggingbot Dec 2, 2025

Choose a reason for hiding this comment

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

it's used to migrate existing users to update the data_type column in the metadata service. Currently the server doesn't know the type of secret (it's encrypted). It needs to know for the deletion API to work: to prevent clients from deleting the default SRP

Copy link
Member Author

Choose a reason for hiding this comment

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

added more notes here

* @param params.updates - Array of objects containing itemId and fields to update.
* @returns A promise that resolves when all updates are complete.
*/
async batchUpdateSecretDataItems(params: {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here.

Copy link
Member Author

Choose a reason for hiding this comment

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

added more notes here

// validate the primary secret data is a mnemonic (SRP)
const primarySecret = secrets[0];
if (primarySecret.type !== SecretType.Mnemonic) {
if (!SecretMetadata.matchesType(results[0].secret, SecretType.Mnemonic)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Since we have Primary type, why don't we use here?

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed here
now we're validating both the .type in the encrypted data and also dataType

authKeyPair: KeyPair;
options?: {
keyringId?: string;
dataType?: EncAccountDataType;
Copy link
Contributor

Choose a reason for hiding this comment

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

we already have type in the params object. Please use it.

Copy link
Member Author

Choose a reason for hiding this comment

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

related question and answer here: #7284 (comment)

…etadata

- Add itemId, dataType, createdAt properties to SecretMetadata class
- Remove SecretDataItemWithMetadata wrapper type
- Update fetchAllSecretData to return SecretMetadata[] directly
- Add tests for storage metadata properties
…EUUID sorting

TIMEUUID strings are not lexicographically sortable. Replace localeCompare
with compareTimeuuid utility that extracts and compares actual timestamps.
);

// Sort: PrimarySrp first, then by createdAt/timestamp (oldest first)
results.sort((a, b) => {
Copy link
Member Author

Choose a reason for hiding this comment

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

sorting is required here even though /get in metadata already does sorting because we need this for the migration since dataType and createdAt could be null thus we'll need a fallback to use the client-side timestamp

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.

3 participants