diff --git a/packages/seedless-onboarding-controller/CHANGELOG.md b/packages/seedless-onboarding-controller/CHANGELOG.md index 50ebd5cec35..c847b6b1ced 100644 --- a/packages/seedless-onboarding-controller/CHANGELOG.md +++ b/packages/seedless-onboarding-controller/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Revert `revokeToken` value as optional in `authenticate` method. ([#7012](https://github.com/MetaMask/core/pull/7012)) +- Renamed `checkIsSeedlessOnboardingUserAuthenticated` to `getIsUserAuthenticated`. ([#7012](https://github.com/MetaMask/core/pull/7012)) + +### Fixed + +- Fixed `InvalidRevokeToken` issue in `refreshAuthTokens` method. ([#7012](https://github.com/MetaMask/core/pull/7012)) + ## [6.0.0] ### Added diff --git a/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.test.ts b/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.test.ts index 136a3d5ddba..fe1d10682e4 100644 --- a/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.test.ts +++ b/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.test.ts @@ -1083,9 +1083,7 @@ describe('SeedlessOnboardingController', () => { }), }, async ({ controller }) => { - expect( - await controller.checkIsSeedlessOnboardingUserAuthenticated(), - ).toBe(true); + expect(await controller.getIsUserAuthenticated()).toBe(true); }, ); }); @@ -1093,18 +1091,13 @@ describe('SeedlessOnboardingController', () => { it('should return false if the user is not authenticated (accessToken is missing)', async () => { await withController( { - state: { - userId, - authConnectionId, - groupedAuthConnectionId, - metadataAccessToken, - revokeToken, - }, + state: getMockInitialControllerState({ + withMockAuthenticatedUser: true, + withoutMockAccessToken: true, // missing accessToken + }), }, async ({ controller }) => { - expect( - await controller.checkIsSeedlessOnboardingUserAuthenticated(), - ).toBe(false); + expect(await controller.getIsUserAuthenticated()).toBe(false); }, ); }); @@ -1112,27 +1105,20 @@ describe('SeedlessOnboardingController', () => { it('should return false if the user is not authenticated (revokeToken is missing)', async () => { await withController( { - state: { - userId, - authConnectionId, - groupedAuthConnectionId, - metadataAccessToken, - accessToken, - }, + state: getMockInitialControllerState({ + withMockAuthenticatedUser: true, + withoutMockRevokeToken: true, // missing revokeToken + }), }, async ({ controller }) => { - expect( - await controller.checkIsSeedlessOnboardingUserAuthenticated(), - ).toBe(false); + expect(await controller.getIsUserAuthenticated()).toBe(false); }, ); }); it('should return false if the user is not authenticated (social login details are missing)', async () => { await withController(async ({ controller }) => { - expect( - await controller.checkIsSeedlessOnboardingUserAuthenticated(), - ).toBe(false); + expect(await controller.getIsUserAuthenticated()).toBe(false); }); }); }); @@ -1288,6 +1274,7 @@ describe('SeedlessOnboardingController', () => { pwEncKey, authKeyPair, MOCK_PASSWORD, + controller.state.revokeToken, ); const expectedVaultValue = await encryptor.decrypt( @@ -1299,7 +1286,7 @@ describe('SeedlessOnboardingController', () => { controller.state.vault as string, ); - expect(expectedVaultValue).toStrictEqual(resultedVaultValue); + expect(resultedVaultValue).toStrictEqual(expectedVaultValue); // should be able to get the hash of the seed phrase backup from the state expect( @@ -1377,6 +1364,36 @@ describe('SeedlessOnboardingController', () => { ); }); + it('should throw error if revokeToken is missing when creating new vault', async () => { + await withController( + { + state: getMockInitialControllerState({ + withMockAuthenticatedUser: true, + withMockAuthPubKey: true, + withoutMockRevokeToken: true, + }), + }, + async ({ controller, toprfClient }) => { + mockcreateLocalKey(toprfClient, MOCK_PASSWORD); + + // persist the local enc key + jest.spyOn(toprfClient, 'persistLocalKey').mockResolvedValueOnce(); + + // encrypt and store the secret data + handleMockSecretDataAdd(); + await expect( + controller.createToprfKeyAndBackupSeedPhrase( + MOCK_PASSWORD, + MOCK_SEED_PHRASE, + MOCK_KEYRING_ID, + ), + ).rejects.toThrow( + SeedlessOnboardingControllerErrorMessage.InvalidRevokeToken, + ); + }, + ); + }); + it('should throw an error if create encryption key fails', async () => { await withController( { @@ -2084,27 +2101,6 @@ describe('SeedlessOnboardingController', () => { ); }); - it('should throw an error if the user is not authenticated', async () => { - await withController( - { - state: { - userId, - authConnectionId, - groupedAuthConnectionId, - metadataAccessToken, - refreshToken, - revokeToken, - nodeAuthTokens: MOCK_NODE_AUTH_TOKENS, - }, - }, - async ({ controller }) => { - await expect(controller.fetchAllSecretData()).rejects.toThrow( - SeedlessOnboardingControllerErrorMessage.InvalidAccessToken, - ); - }, - ); - }); - it('should be able to fetch seed phrases with cached encryption key without providing password', async () => { const mockToprfEncryptor = createMockToprfEncryptor(); @@ -3839,6 +3835,7 @@ describe('SeedlessOnboardingController', () => { initialPwEncKey, initialAuthKeyPair, OLD_PASSWORD, + revokeToken, ); MOCK_VAULT = mockResult.encryptedMockVault; @@ -5331,6 +5328,24 @@ describe('SeedlessOnboardingController', () => { }); }); + it('should return true if access token is missing', async () => { + await withController( + { + state: getMockInitialControllerState({ + withMockAuthenticatedUser: true, + withoutMockAccessToken: true, + }), + }, + async ({ controller }) => { + // Restore the original implementation to test the real logic + jest.spyOn(controller, 'checkAccessTokenExpired').mockRestore(); + + const result = controller.checkAccessTokenExpired(); + expect(result).toBe(true); + }, + ); + }); + it('should return true if token has invalid format', async () => { await withController( { @@ -5350,6 +5365,117 @@ describe('SeedlessOnboardingController', () => { }); }); + describe('#getAccessTokenAndRevokeToken', () => { + const MOCK_PASSWORD = 'mock-password'; + + it('should retrieve the access token and revoke token from the vault if it is not available in the state', async () => { + const mockToprfEncryptor = createMockToprfEncryptor(); + const MOCK_ENCRYPTION_KEY = + mockToprfEncryptor.deriveEncKey(MOCK_PASSWORD); + const MOCK_PASSWORD_ENCRYPTION_KEY = + mockToprfEncryptor.derivePwEncKey(MOCK_PASSWORD); + const MOCK_AUTH_KEY_PAIR = + mockToprfEncryptor.deriveAuthKeyPair(MOCK_PASSWORD); + + const mockResult = await createMockVault( + MOCK_ENCRYPTION_KEY, + MOCK_PASSWORD_ENCRYPTION_KEY, + MOCK_AUTH_KEY_PAIR, + MOCK_PASSWORD, + ); + + const MOCK_VAULT = mockResult.encryptedMockVault; + const MOCK_VAULT_ENCRYPTION_KEY = mockResult.vaultEncryptionKey; + const MOCK_VAULT_ENCRYPTION_SALT = mockResult.vaultEncryptionSalt; + + await withController( + { + state: getMockInitialControllerState({ + withMockAuthenticatedUser: true, + withoutMockAccessToken: true, + withoutMockRevokeToken: true, + vault: MOCK_VAULT, + vaultEncryptionKey: MOCK_VAULT_ENCRYPTION_KEY, + vaultEncryptionSalt: MOCK_VAULT_ENCRYPTION_SALT, + }), + }, + async ({ controller, toprfClient }) => { + // fetch and decrypt the secret data + mockRecoverEncKey(toprfClient, MOCK_PASSWORD); + + // mock the secret data get + jest + .spyOn(toprfClient, 'fetchAllSecretDataItems') + .mockResolvedValueOnce([ + stringToBytes( + JSON.stringify({ + data: bytesToBase64(MOCK_SEED_PHRASE), + timestamp: 1234567890, + type: SecretType.Mnemonic, + version: 'v1', + }), + ), + stringToBytes( + JSON.stringify({ + data: bytesToBase64(MOCK_PRIVATE_KEY), + timestamp: 1234567890, + type: SecretType.PrivateKey, + version: 'v1', + }), + ), + ]); + + const secretData = await controller.fetchAllSecretData(MOCK_PASSWORD); + expect(secretData).toBeDefined(); + expect(secretData).toHaveLength(2); + expect(secretData[0].type).toStrictEqual(SecretType.Mnemonic); + expect(secretData[0].data).toStrictEqual(MOCK_SEED_PHRASE); + expect(secretData[1].type).toStrictEqual(SecretType.PrivateKey); + expect(secretData[1].data).toStrictEqual(MOCK_PRIVATE_KEY); + + // expect(mockSecretDataGet.isDone()).toBe(true); + }, + ); + }); + + it('should throw error if access token is not available either in the state or the vault', async () => { + await withController( + { + state: getMockInitialControllerState({ + withMockAuthenticatedUser: true, + withoutMockAccessToken: true, + }), + }, + async ({ controller, toprfClient }) => { + // assert that the vault is not available in the state + expect(controller.state.vault).toBeUndefined(); + + // fetch and decrypt the secret data + mockRecoverEncKey(toprfClient, MOCK_PASSWORD); + + jest + .spyOn(toprfClient, 'fetchAllSecretDataItems') + .mockResolvedValueOnce([ + stringToBytes( + JSON.stringify({ + data: 'value', + timestamp: 1234567890, + type: 'mnemonic', + version: 'v1', + }), + ), + ]); + + await expect( + controller.fetchAllSecretData(MOCK_PASSWORD), + ).rejects.toThrow( + SeedlessOnboardingControllerErrorMessage.InvalidAccessToken, + ); + }, + ); + }); + }); + describe('renewRefreshToken', () => { const MOCK_PASSWORD = 'mock-password'; const MOCK_REVOKE_TOKEN = 'newRevokeToken'; diff --git a/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.ts b/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.ts index 00316f970d3..5811e04a27f 100644 --- a/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.ts +++ b/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.ts @@ -22,7 +22,6 @@ import { secp256k1 } from '@noble/curves/secp256k1'; import { Mutex } from 'async-mutex'; import { - assertIsAuthUserInfoValid, assertIsPasswordOutdatedCacheValid, assertIsSeedlessOnboardingUserAuthenticated, assertIsValidVaultData, @@ -45,7 +44,6 @@ import type { SeedlessOnboardingControllerState, AuthenticatedUserDetails, SocialBackupsMetadata, - SRPBackedUpUserDetails, VaultEncryptor, RefreshJWTToken, RevokeRefreshToken, @@ -365,7 +363,7 @@ export class SeedlessOnboardingController extends BaseController< groupedAuthConnectionId?: string; socialLoginEmail?: string; refreshToken: string; - revokeToken: string; + revokeToken?: string; skipLock?: boolean; }) { const doAuthenticateWithNodes = async () => { @@ -399,8 +397,10 @@ export class SeedlessOnboardingController extends BaseController< state.socialLoginEmail = socialLoginEmail; state.metadataAccessToken = metadataAccessToken; state.refreshToken = refreshToken; - // Temporarily store revoke token & access token in state for later vault creation - state.revokeToken = revokeToken; + if (revokeToken) { + // Temporarily store revoke token & access token in state for later vault creation + state.revokeToken = revokeToken; + } state.accessToken = accessToken; // we will check if the controller state is properly set with the authenticated user info @@ -892,7 +892,7 @@ export class SeedlessOnboardingController extends BaseController< } } - assertIsAuthUserInfoValid(this.state); + this.#assertIsAuthenticatedUser(this.state); const { nodeAuthTokens, authConnectionId, @@ -943,20 +943,21 @@ export class SeedlessOnboardingController extends BaseController< /** * Check if the user is authenticated with the seedless onboarding flow by checking the token values in the state. * + * This method will check the `accessToken` and `revokeToken` in the state, besides the social login authentication details. + * If both are present, the user is authenticated. + * If either is missing, the user is not authenticated. + * + * This method is useful when we want to check if the state has valid authenticated user details to perform vault creations. + * * @returns True if the user is authenticated, false otherwise. */ - async checkIsSeedlessOnboardingUserAuthenticated(): Promise { - let isAuthenticated = false; + async getIsUserAuthenticated(): Promise { try { - assertIsSeedlessOnboardingUserAuthenticated(this.state); - isAuthenticated = true; + this.#assertIsAuthenticatedUser(this.state); + return Boolean(this.state.accessToken) && Boolean(this.state.revokeToken); } catch { - isAuthenticated = false; + return false; } - this.update((state) => { - state.isSeedlessOnboardingUserAuthenticated = isAuthenticated; - }); - return isAuthenticated; } #setUnlocked(): void { @@ -1100,8 +1101,12 @@ export class SeedlessOnboardingController extends BaseController< * @returns The authentication public key. */ #recoverAuthPubKey(): SEC1EncodedPublicKey { - this.#assertIsSRPBackedUpUser(this.state); const { authPubKey } = this.state; + if (!authPubKey) { + throw new Error( + SeedlessOnboardingControllerErrorMessage.SRPNotBackedUpError, + ); + } return base64ToBytes(authPubKey); } @@ -1116,7 +1121,7 @@ export class SeedlessOnboardingController extends BaseController< async #recoverEncKey( password: string, ): Promise> { - assertIsAuthUserInfoValid(this.state); + this.#assertIsAuthenticatedUser(this.state); const { nodeAuthTokens, authConnectionId, @@ -1529,7 +1534,8 @@ export class SeedlessOnboardingController extends BaseController< }): Promise { this.#assertIsAuthenticatedUser(this.state); - const { revokeToken, accessToken } = this.state; + const { accessToken, revokeToken } = + await this.#getAccessTokenAndRevokeToken(password); const vaultData: DeserializedVaultData = { toprfAuthKeyPair: rawToprfAuthKeyPair, @@ -1601,6 +1607,48 @@ export class SeedlessOnboardingController extends BaseController< }); } + /** + * Get the access token and revoke token from the state or the vault. + * + * @param password - The password to decrypt the vault. + * @returns The access token and revoke token. + */ + async #getAccessTokenAndRevokeToken( + password: string, + ): Promise<{ accessToken: string; revokeToken: string }> { + let { accessToken, revokeToken } = this.state; + // `accessToken` and `revokeToken` are both available in the state, `ONLY` when the wallet (vault) is unlocked + // or during the period between the social authentication and the vault creation during the onboarding flow. + if (accessToken && revokeToken) { + return { accessToken, revokeToken }; + } + + // if `password` is provided to decrypt the vault, decrypt the vault and get the access token and revoke token from the vault + if (this.state.vault) { + // if the access token or revoke token is not available in the state, decrypt the vault and get the access token and revoke token from the vault + const { vaultData } = await this.#decryptAndParseVaultData({ password }); + accessToken = accessToken || vaultData.accessToken; + revokeToken = revokeToken || vaultData.revokeToken; + } + + // we should always throw an error if the access token or revoke token is not available + // to prevent the caller from using the controller in an invalid state + + if (!accessToken) { + throw new Error( + SeedlessOnboardingControllerErrorMessage.InvalidAccessToken, + ); + } + + if (!revokeToken) { + throw new Error( + SeedlessOnboardingControllerErrorMessage.InvalidRevokeToken, + ); + } + + return { accessToken, revokeToken }; + } + /** * Lock the controller mutex before executing the given function, * and release it after the function is resolved or after an @@ -1693,16 +1741,6 @@ export class SeedlessOnboardingController extends BaseController< } } - #assertIsSRPBackedUpUser( - value: unknown, - ): asserts value is SRPBackedUpUserDetails { - if (!this.state.authPubKey) { - throw new Error( - SeedlessOnboardingControllerErrorMessage.SRPNotBackedUpError, - ); - } - } - /** * Assert that the password is in sync with the global password. * @@ -1768,7 +1806,7 @@ export class SeedlessOnboardingController extends BaseController< */ async refreshAuthTokens(): Promise { this.#assertIsAuthenticatedUser(this.state); - const { refreshToken, revokeToken } = this.state; + const { refreshToken } = this.state; const res = await this.#refreshJWTToken({ connection: this.state.authConnection, @@ -1783,6 +1821,7 @@ export class SeedlessOnboardingController extends BaseController< try { const { idTokens, accessToken, metadataAccessToken } = res; // re-authenticate with the new id tokens to set new node auth tokens + // NOTE: here we can't provide the `revokeToken` value to the `authenticate` method because `refreshAuthTokens` method can be called when the wallet (vault) is locked await this.authenticate({ idTokens, accessToken, @@ -1792,7 +1831,6 @@ export class SeedlessOnboardingController extends BaseController< groupedAuthConnectionId: this.state.groupedAuthConnectionId, userId: this.state.userId, refreshToken, - revokeToken, skipLock: true, }); } catch (error) { @@ -1824,11 +1862,6 @@ export class SeedlessOnboardingController extends BaseController< password, encryptionKey: vaultEncryptionKey, }); - if (!revokeToken) { - throw new Error( - SeedlessOnboardingControllerErrorMessage.InvalidRevokeToken, - ); - } const { newRevokeToken, newRefreshToken } = await this.#renewRefreshToken( { @@ -1990,7 +2023,6 @@ export class SeedlessOnboardingController extends BaseController< const isNodeAuthTokenExpired = this.checkNodeAuthTokenExpired(); const isMetadataAccessTokenExpired = this.checkMetadataAccessTokenExpired(); - // access token is only accessible when the vault is unlocked // so skip the check if the vault is locked let isAccessTokenExpired = false; @@ -2078,6 +2110,9 @@ export class SeedlessOnboardingController extends BaseController< try { this.#assertIsAuthenticatedUser(this.state); const { accessToken } = this.state; + if (!accessToken) { + return true; // Consider missing token as expired + } const decodedToken = decodeJWTToken(accessToken); return decodedToken.exp < Math.floor(Date.now() / 1000); } catch { diff --git a/packages/seedless-onboarding-controller/src/assertions.test.ts b/packages/seedless-onboarding-controller/src/assertions.test.ts index 3b50f70870a..998aff9bb9f 100644 --- a/packages/seedless-onboarding-controller/src/assertions.test.ts +++ b/packages/seedless-onboarding-controller/src/assertions.test.ts @@ -81,51 +81,23 @@ describe('assertIsValidVaultData', () => { }).toThrow(SeedlessOnboardingControllerErrorMessage.InvalidVaultData); }); - it('should throw when revokeToken exists but is not a string or undefined', () => { - const invalidData = { - ...createValidVaultData(), - revokeToken: 789, - }; - - expect(() => { - assertIsValidVaultData(invalidData); - }).toThrow(SeedlessOnboardingControllerErrorMessage.InvalidVaultData); - - const invalidData2 = { - ...createValidVaultData(), - revokeToken: null, - }; - - expect(() => { - assertIsValidVaultData(invalidData2); - }).toThrow(SeedlessOnboardingControllerErrorMessage.InvalidVaultData); - - const invalidData3 = { - ...createValidVaultData(), - revokeToken: {}, - }; - - expect(() => { - assertIsValidVaultData(invalidData3); - }).toThrow(SeedlessOnboardingControllerErrorMessage.InvalidVaultData); - }); - it('should throw when accessToken is missing or not a string', () => { const invalidData = createValidVaultData(); delete (invalidData as Record).accessToken; expect(() => { assertIsValidVaultData(invalidData); - }).toThrow(SeedlessOnboardingControllerErrorMessage.InvalidVaultData); + }).toThrow(SeedlessOnboardingControllerErrorMessage.InvalidAccessToken); const invalidData2 = { ...createValidVaultData(), + revokeToken: 'MOCK_REVOKE_TOKEN', accessToken: 999, }; expect(() => { assertIsValidVaultData(invalidData2); - }).toThrow(SeedlessOnboardingControllerErrorMessage.InvalidVaultData); + }).toThrow(SeedlessOnboardingControllerErrorMessage.InvalidAccessToken); }); }); @@ -138,50 +110,6 @@ describe('assertIsValidVaultData', () => { }).not.toThrow(); }); - it('should not throw when revokeToken is undefined', () => { - const validData = { - ...createValidVaultData(), - revokeToken: undefined, - }; - - expect(() => { - assertIsValidVaultData(validData); - }).not.toThrow(); - }); - - it('should not throw when revokeToken is a valid string', () => { - const validData = { - ...createValidVaultData(), - revokeToken: 'valid_revoke_token', - }; - - expect(() => { - assertIsValidVaultData(validData); - }).not.toThrow(); - }); - - it('should not throw when revokeToken property is missing entirely', () => { - const validData = createValidVaultData(); - delete (validData as Record).revokeToken; - - expect(() => { - assertIsValidVaultData(validData); - }).not.toThrow(); - }); - - it('should not throw with minimal valid vault data', () => { - const minimalValidData = { - toprfEncryptionKey: 'key1', - toprfPwEncryptionKey: 'key2', - toprfAuthKeyPair: 'keyPair', - accessToken: 'token', - }; - - expect(() => { - assertIsValidVaultData(minimalValidData); - }).not.toThrow(); - }); - it('should not throw with extra properties in valid vault data', () => { const validDataWithExtras = { ...createValidVaultData(), diff --git a/packages/seedless-onboarding-controller/src/assertions.ts b/packages/seedless-onboarding-controller/src/assertions.ts index df81a7ec0be..e8a35539340 100644 --- a/packages/seedless-onboarding-controller/src/assertions.ts +++ b/packages/seedless-onboarding-controller/src/assertions.ts @@ -2,17 +2,14 @@ import { SeedlessOnboardingControllerErrorMessage } from './constants'; import type { AuthenticatedUserDetails, VaultData } from './types'; /** - * Check if the provided value is a valid user info. + * Check if the provided value is a valid authenticated user. * * @param value - The value to check. - * @throws If the value is not a valid user info. + * @throws If the value is not a valid authenticated user. */ -export function assertIsAuthUserInfoValid( +export function assertIsSeedlessOnboardingUserAuthenticated( value: unknown, -): asserts value is Pick< - AuthenticatedUserDetails, - 'authConnectionId' | 'userId' | 'nodeAuthTokens' -> { +): asserts value is AuthenticatedUserDetails { if ( !value || typeof value !== 'object' || @@ -36,18 +33,6 @@ export function assertIsAuthUserInfoValid( SeedlessOnboardingControllerErrorMessage.InsufficientAuthToken, ); } -} - -/** - * Check if the provided value is a valid authenticated user. - * - * @param value - The value to check. - * @throws If the value is not a valid authenticated user. - */ -export function assertIsSeedlessOnboardingUserAuthenticated( - value: unknown, -): asserts value is AuthenticatedUserDetails { - assertIsAuthUserInfoValid(value); if (!('refreshToken' in value) || typeof value.refreshToken !== 'string') { throw new Error( @@ -62,18 +47,6 @@ export function assertIsSeedlessOnboardingUserAuthenticated( SeedlessOnboardingControllerErrorMessage.InvalidMetadataAccessToken, ); } - - if (!('revokeToken' in value) || typeof value.revokeToken !== 'string') { - throw new Error( - SeedlessOnboardingControllerErrorMessage.InvalidRevokeToken, - ); - } - - if (!('accessToken' in value) || typeof value.accessToken !== 'string') { - throw new Error( - SeedlessOnboardingControllerErrorMessage.InvalidAccessToken, - ); - } } /** @@ -116,14 +89,20 @@ export function assertIsValidVaultData( !('toprfPwEncryptionKey' in value) || // toprfPwEncryptionKey is not defined typeof value.toprfPwEncryptionKey !== 'string' || // toprfPwEncryptionKey is not a string !('toprfAuthKeyPair' in value) || // toprfAuthKeyPair is not defined - typeof value.toprfAuthKeyPair !== 'string' || // toprfAuthKeyPair is not a string - // revoke token exists but is not a string and is not undefined - ('revokeToken' in value && - typeof value.revokeToken !== 'string' && - value.revokeToken !== undefined) || - !('accessToken' in value) || // accessToken is not defined - typeof value.accessToken !== 'string' // accessToken is not a string + typeof value.toprfAuthKeyPair !== 'string' // toprfAuthKeyPair is not a string ) { throw new Error(SeedlessOnboardingControllerErrorMessage.InvalidVaultData); } + + if (!('revokeToken' in value) || typeof value.revokeToken !== 'string') { + throw new Error( + SeedlessOnboardingControllerErrorMessage.InvalidRevokeToken, + ); + } + + if (!('accessToken' in value) || typeof value.accessToken !== 'string') { + throw new Error( + SeedlessOnboardingControllerErrorMessage.InvalidAccessToken, + ); + } } diff --git a/packages/seedless-onboarding-controller/src/types.ts b/packages/seedless-onboarding-controller/src/types.ts index d4f73cbc3c7..4788aa10d21 100644 --- a/packages/seedless-onboarding-controller/src/types.ts +++ b/packages/seedless-onboarding-controller/src/types.ts @@ -77,16 +77,6 @@ export type AuthenticatedUserDetails = { * The refresh token used to refresh expired nodeAuthTokens. */ refreshToken: string; - - /** - * The revoke token used to revoke refresh token and get new refresh token and new revoke token. - */ - revokeToken: string; - - /** - * The access token used for pairing with profile sync auth service and to access other services. - */ - accessToken: string; }; export type SRPBackedUpUserDetails = { @@ -369,7 +359,7 @@ export type VaultData = { * The revoke token to revoke refresh token and get new refresh token and new revoke token. * The revoke token may no longer be available after a large number of password changes. In this case, re-authentication is advised. */ - revokeToken?: string; + revokeToken: string; /** * The access token used for pairing with profile sync auth service and to access other services. */