diff --git a/example/src/test_tanker.ts b/example/src/test_tanker.ts index 83b4f19..37661b2 100644 --- a/example/src/test_tanker.ts +++ b/example/src/test_tanker.ts @@ -11,7 +11,11 @@ import { getVerificationCode, toggleSessionCertificates, } from './admin'; -import { InvalidArgument, InvalidVerification } from '@tanker/errors'; +import { + InvalidArgument, + InvalidVerification, + IdentityAlreadyAttached, +} from '@tanker/errors'; import { createTanker, clearTankerDataDirs } from './tests'; import base64 from 'react-native-base64'; @@ -212,6 +216,33 @@ export const tankerTests = () => { await tanker.verifyProvisionalIdentity({ email, verificationCode }); }); + it('throws when attaching an already attached provisional identity', async () => { + await tanker.start(identity); + await tanker.registerIdentity({ + passphrase: 'ice cold water', + }); + const email = 'bob@burger.io'; + const provIdentity = await createProvisionalIdentity(email); + const result = await tanker.attachProvisionalIdentity(provIdentity); + expect(result.verificationMethod).deep.eq({ type: 'email', email }); + + const verificationCode = await getVerificationCode(email); + await tanker.verifyProvisionalIdentity({ email, verificationCode }); + + const other = await createTanker(); + await other.start(await createIdentity()); + await other.registerIdentity({ passphrase: 'otherpass' }); + other.attachProvisionalIdentity(provIdentity); + expect(result.status).eq(Tanker.statuses.IDENTITY_VERIFICATION_NEEDED); + const verificationCode2 = await getVerificationCode(email); + await expect( + other.verifyProvisionalIdentity({ + email, + verificationCode: verificationCode2, + }) + ).to.be.rejectedWith(IdentityAlreadyAttached); + }); + it('can skip provisional identity verification', async () => { const email = 'bob@bargor.io'; const provIdentity = await createProvisionalIdentity(email); diff --git a/ios/Utils+Private.m b/ios/Utils+Private.m index cd80ca4..23c3d4c 100644 --- a/ios/Utils+Private.m +++ b/ios/Utils+Private.m @@ -136,6 +136,8 @@ void rejectWithError(RCTPromiseRejectBlock _Nonnull reject, NSError* _Nonnull er return @"DEVICE_REVOKED"; case TKRErrorUpgradeRequired: return @"UPGRADE_REQUIRED"; + case TKRErrorIdentityAlreadyAttached: + return @"IDENTITY_ALREADY_ATTACHED"; default: return @"UNKNOWN_ERROR"; } diff --git a/src/errors.ts b/src/errors.ts index 231fdcd..bc89285 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -12,6 +12,7 @@ import { DeviceRevoked, Conflict, UpgradeRequired, + IdentityAlreadyAttached, TankerError, } from '@tanker/errors'; @@ -33,6 +34,7 @@ export const errors = { TankerError, TooManyAttempts, UpgradeRequired, + IdentityAlreadyAttached, }; function translateException(e: any): never { @@ -67,6 +69,8 @@ function translateException(e: any): never { throw new Conflict(e.message); case 'UPGRADE_REQUIRED': throw new UpgradeRequired(e.message); + case 'IDENTITY_ALREADY_ATTACHED': + throw new IdentityAlreadyAttached(e.message); default: // This could be something else than a TankerException, do not wrap or convert to avoid losing information throw e;