Skip to content

Commit

Permalink
Merge pull request #700 from magiclabs/jerryliu-PDEEXP-70-add-whitela…
Browse files Browse the repository at this point in the history
…bel-events-in-magic-js-add-whitelabel-events-in-magic-js

Enable whitelabel for updateEmail flow
  • Loading branch information
Ethella authored Jan 19, 2024
2 parents 324f424 + db78e9a commit 8afc7dc
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 120 deletions.
29 changes: 27 additions & 2 deletions packages/@magic-sdk/provider/src/modules/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import {
UpdateEmailConfiguration,
DeviceVerificationEventEmit,
LoginWithEmailOTPEventEmit,
UpdateEmailEventHandlers,
UpdateEmailEventEmit,
RecencyCheckEventEmit,
} from '@magic-sdk/types';
import { BaseModule } from './base-module';
import { createJsonRpcRequestPayload } from '../core/json-rpc';
import { SDKEnvironment } from '../core/sdk-environment';
import { UpdateEmailEvents } from './user';
import { isMajorVersionAtLeast } from '../util/version-check';
import { createDeprecationWarning } from '../core/sdk-exceptions';

Expand Down Expand Up @@ -141,6 +143,29 @@ export class AuthModule extends BaseModule {
this.sdk.testMode ? MagicPayloadMethod.UpdateEmailTestMode : MagicPayloadMethod.UpdateEmail,
[{ email, showUI }],
);
return this.request<string | null, UpdateEmailEvents>(requestPayload);

const handle = this.request<string | null, UpdateEmailEventHandlers>(requestPayload);

if (!showUI) {
handle.on(RecencyCheckEventEmit.Retry, () => {
this.createIntermediaryEvent(RecencyCheckEventEmit.Retry, requestPayload.id as any)();
});
handle.on(RecencyCheckEventEmit.Cancel, () => {
this.createIntermediaryEvent(RecencyCheckEventEmit.Cancel, requestPayload.id as any)();
});
handle.on(RecencyCheckEventEmit.VerifyEmailOtp, (otp: string) => {
this.createIntermediaryEvent(RecencyCheckEventEmit.VerifyEmailOtp, requestPayload.id as any)(otp);
});
handle.on(UpdateEmailEventEmit.RetryWithNewEmail, (newEmail?) => {
this.createIntermediaryEvent(UpdateEmailEventEmit.RetryWithNewEmail, requestPayload.id as any)(newEmail);
});
handle.on(UpdateEmailEventEmit.Cancel, () => {
this.createIntermediaryEvent(UpdateEmailEventEmit.Cancel, requestPayload.id as any)();
});
handle.on(UpdateEmailEventEmit.VerifyEmailOtp, (otp: string) => {
this.createIntermediaryEvent(UpdateEmailEventEmit.VerifyEmailOtp, requestPayload.id as any)(otp);
});
}
return handle;
}
}
24 changes: 0 additions & 24 deletions packages/@magic-sdk/provider/src/modules/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
MagicPayloadMethod,
MagicUserMetadata,
GenerateIdTokenConfiguration,
UpdateEmailConfiguration,
UserInfo,
RequestUserInfoScope,
RecoverAccountConfiguration,
Expand All @@ -17,14 +16,6 @@ import { ProductConsolidationMethodRemovalVersions } from './auth';
import { clearDeviceShares } from '../util/device-share-web-crypto';
import { createPromiEvent } from '../util';

export type UpdateEmailEvents = {
'email-sent': () => void;
'email-not-deliverable': () => void;
'old-email-confirmed': () => void;
'new-email-confirmed': () => void;
retry: () => void;
};

type UserLoggedOutCallback = (loggedOut: boolean) => void;

export class UserModule extends BaseModule {
Expand Down Expand Up @@ -141,21 +132,6 @@ export class UserModule extends BaseModule {
return this.request<MagicUserMetadata>(requestPayload);
}

// Deprecating
public updateEmail(configuration: UpdateEmailConfiguration) {
createDeprecationWarning({
method: 'user.updateEmail()',
removalVersions: ProductConsolidationMethodRemovalVersions,
useInstead: 'auth.updateEmailWithUI()',
}).log();
const { email, showUI = true } = configuration;
const requestPayload = createJsonRpcRequestPayload(
this.sdk.testMode ? MagicPayloadMethod.UpdateEmailTestMode : MagicPayloadMethod.UpdateEmail,
[{ email, showUI }],
);
return this.request<string | null, UpdateEmailEvents>(requestPayload);
}

public onUserLoggedOut(callback: UserLoggedOutCallback): void {
this.userLoggedOutCallbacks.push(callback);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,27 @@ test('Accepts a `showUI` parameter', async () => {
const magic = createMagicSDK();
magic.auth.request = jest.fn();

await magic.auth.updateEmailWithUI({ email: 'test', showUI: false });
await magic.auth.updateEmailWithUI({ email: 'test' });

const requestPayload = magic.auth.request.mock.calls[0][0];
expect(requestPayload.jsonrpc).toBe('2.0');
expect(requestPayload.method).toBe('magic_auth_update_email');
expect(requestPayload.params).toEqual([{ email: 'test', showUI: false }]);
expect(requestPayload.params).toEqual([{ email: 'test', showUI: true }]);
});

test('If `testMode` is enabled, testing-specific RPC method is used', async () => {
const magic = createMagicSDKTestMode();
magic.auth.request = jest.fn();

await magic.auth.updateEmailWithUI({ email: 'test', showUI: false });
await magic.auth.updateEmailWithUI({ email: 'test' });

const requestPayload = magic.auth.request.mock.calls[0][0];
expect(requestPayload.jsonrpc).toBe('2.0');
expect(requestPayload.method).toBe('magic_auth_update_email_testing_mode');
expect(requestPayload.params).toEqual([{ email: 'test', showUI: false }]);
expect(requestPayload.params).toEqual([{ email: 'test', showUI: true }]);
});

test('method should return a PromiEvent', () => {
const magic = createMagicSDK();
expect(isPromiEvent(magic.auth.updateEmailWithUI({ email: 'test', showUI: false }))).toBeTruthy();
expect(isPromiEvent(magic.auth.updateEmailWithUI({ email: 'test', showUI: true }))).toBeTruthy();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import browserEnv from '@ikscodes/browser-env';
import { RecencyCheckEventEmit, UpdateEmailEventEmit } from '@magic-sdk/types';
import { createMagicSDK } from '../../../factories';

beforeEach(() => {
browserEnv.restore();
});

test('Generate JSON RPC request payload with method `magic_auth_update_email` whitelabel and start recency check', async () => {
const magic = createMagicSDK();
magic.auth.overlay.post = jest.fn().mockImplementation(() => new Promise(() => {}));
const createIntermediaryEventFn = jest.fn();
magic.auth.createIntermediaryEvent = jest.fn().mockImplementation(() => createIntermediaryEventFn);

const handle = magic.auth.updateEmailWithUI({ email: 'test', showUI: false });

const troll_otp = '123456';
handle.emit(RecencyCheckEventEmit.VerifyEmailOtp, troll_otp);
handle.emit(RecencyCheckEventEmit.Cancel);

const verifyEvent = magic.auth.createIntermediaryEvent.mock.calls[0];
expect(verifyEvent[0]).toBe(RecencyCheckEventEmit.VerifyEmailOtp);
expect(createIntermediaryEventFn.mock.calls[0][0]).toBe(troll_otp);

const intermediaryEventSecondMethod = magic.auth.createIntermediaryEvent.mock.calls[1][0];
expect(intermediaryEventSecondMethod).toBe(RecencyCheckEventEmit.Cancel);
});

test('Whitelabel `magic_auth_update_email`, recency check Retry event', async () => {
const magic = createMagicSDK();
magic.auth.overlay.post = jest.fn().mockImplementation(() => new Promise(() => {}));
const createIntermediaryEventFn = jest.fn();
magic.auth.createIntermediaryEvent = jest.fn().mockImplementation(() => createIntermediaryEventFn);

const handle = magic.auth.updateEmailWithUI({ email: 'test', showUI: false });

handle.emit(RecencyCheckEventEmit.Retry);

const intermediaryEventSecondMethod = magic.auth.createIntermediaryEvent.mock.calls[0][0];
expect(intermediaryEventSecondMethod).toBe(RecencyCheckEventEmit.Retry);
});

test('Whitelabel `magic_auth_update_email`, Update Email, fire retry with Email event', async () => {
const magic = createMagicSDK();
magic.auth.overlay.post = jest.fn().mockImplementation(() => new Promise(() => {}));
const createIntermediaryEventFn = jest.fn();
magic.auth.createIntermediaryEvent = jest.fn().mockImplementation(() => createIntermediaryEventFn);

const handle = magic.auth.updateEmailWithUI({ email: 'test', showUI: false });

const alternativeEmail = '[email protected]';

handle.emit(UpdateEmailEventEmit.RetryWithNewEmail, alternativeEmail);

const intermediaryEventFirstMethod = magic.auth.createIntermediaryEvent.mock.calls[0][0];
expect(intermediaryEventFirstMethod).toBe(UpdateEmailEventEmit.RetryWithNewEmail);
expect(createIntermediaryEventFn.mock.calls[0][0]).toBe(alternativeEmail);
});

test('Generate JSON RPC request payload with method `magic_auth_update_email` whitelabel and start verify Email otp ', async () => {
const magic = createMagicSDK();
magic.auth.overlay.post = jest.fn().mockImplementation(() => new Promise(() => {}));
const createIntermediaryEventFn = jest.fn();
magic.auth.createIntermediaryEvent = jest.fn().mockImplementation(() => createIntermediaryEventFn);

const handle = magic.auth.updateEmailWithUI({ email: 'test', showUI: false });

const troll_otp = '123456';
handle.emit(UpdateEmailEventEmit.VerifyEmailOtp, troll_otp);
handle.emit(UpdateEmailEventEmit.Cancel);

const verifyEvent = magic.auth.createIntermediaryEvent.mock.calls[0];
expect(verifyEvent[0]).toBe(UpdateEmailEventEmit.VerifyEmailOtp);
expect(createIntermediaryEventFn.mock.calls[0][0]).toBe(troll_otp);

const intermediaryEventSecondMethod = magic.auth.createIntermediaryEvent.mock.calls[1][0];
expect(intermediaryEventSecondMethod).toBe(UpdateEmailEventEmit.Cancel);
});

This file was deleted.

62 changes: 62 additions & 0 deletions packages/@magic-sdk/types/src/modules/auth-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,65 @@ export enum DeviceVerificationEventOnReceived {
DeviceVerificationLinkExpired = 'device-verification-link-expired',
DeviceVerificationEmailSent = 'device-verification-email-sent',
}

/**
* Update Email
*/

type RecencyCheckEventHandlers = {
[RecencyCheckEventOnReceived.PrimaryAuthFactorNeedsVerification]: () => void;
[RecencyCheckEventOnReceived.PrimaryAuthFactorVerified]: () => void;
[RecencyCheckEventOnReceived.InvalidEmailOtp]: () => void;
[RecencyCheckEventOnReceived.EmailNotDeliverable]: () => void;
[RecencyCheckEventOnReceived.EmailExpired]: () => void;
[RecencyCheckEventOnReceived.EmailSent]: () => void;

[RecencyCheckEventEmit.Cancel]: () => void;
[RecencyCheckEventEmit.Retry]: () => void;
[RecencyCheckEventEmit.VerifyEmailOtp]: (otp: string) => void;
};

export type UpdateEmailEventHandlers = {
[UpdateEmailEventOnReceived.NewAuthFactorNeedsVerification]: () => void;
[UpdateEmailEventOnReceived.EmailUpdated]: () => void;
[UpdateEmailEventOnReceived.InvalidEmailOtp]: () => void;
[UpdateEmailEventOnReceived.EmailNotDeliverable]: () => void;
[UpdateEmailEventOnReceived.EmailExpired]: () => void;
[UpdateEmailEventOnReceived.EmailSent]: () => void;
[UpdateEmailEventOnReceived.InvalidEmail]: () => void;

[UpdateEmailEventEmit.Cancel]: () => void;
[UpdateEmailEventEmit.RetryWithNewEmail]: (email?: string) => void;
[UpdateEmailEventEmit.VerifyEmailOtp]: (otp: string) => void;
} & RecencyCheckEventHandlers;

export enum RecencyCheckEventEmit {
Retry = 'Recency/auth-factor-retry',
Cancel = 'Recency/auth-factor-verification-cancel',
VerifyEmailOtp = 'Recency/auth-factor-verify-email-otp',
}

export enum RecencyCheckEventOnReceived {
PrimaryAuthFactorNeedsVerification = 'Recency/auth-factor-needs-verification',
PrimaryAuthFactorVerified = 'Recency/auth-factor-verified',
InvalidEmailOtp = 'Recency/auth-factor-invalid-email-otp',
EmailExpired = 'Recency/auth-factor-verification-email-expired',
EmailSent = 'Recency/auth-factor-verification-email-sent',
EmailNotDeliverable = 'Recency/auth-factor-verification-email-not-deliverable',
}

export enum UpdateEmailEventEmit {
RetryWithNewEmail = 'UpdateEmail/retry-with-new-email',
Cancel = 'UpdateEmail/new-email-verification-cancel',
VerifyEmailOtp = 'UpdateEmail/new-email-verify-otp',
}

export enum UpdateEmailEventOnReceived {
NewAuthFactorNeedsVerification = 'UpdateEmail/new-email-needs-verification',
EmailUpdated = 'UpdateEmail/email-updated',
InvalidEmailOtp = 'UpdateEmail/new-email-invalid-email-otp',
EmailExpired = 'UpdateEmail/new-email-verification-email-expired',
EmailSent = 'UpdateEmail/new-email-verification-email-sent',
EmailNotDeliverable = 'UpdateEmail/new-email-verification-email-not-deliverable',
InvalidEmail = 'UpdateEmail/new-email-invalid',
}
12 changes: 11 additions & 1 deletion packages/@magic-sdk/types/src/modules/intermediary-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import {
LoginWithEmailOTPEventOnReceived,
LoginWithMagicLinkEventEmit,
LoginWithMagicLinkEventOnReceived,
RecencyCheckEventEmit,
RecencyCheckEventOnReceived,
UpdateEmailEventEmit,
UpdateEmailEventOnReceived,
} from './auth-types';

export type IntermediaryEvents =
Expand All @@ -16,4 +20,10 @@ export type IntermediaryEvents =
| `${LoginWithMagicLinkEventOnReceived}`
// Device Verification
| `${DeviceVerificationEventEmit}`
| `${DeviceVerificationEventOnReceived}`;
| `${DeviceVerificationEventOnReceived}`
// Recency Check
| `${RecencyCheckEventEmit}`
| `${RecencyCheckEventOnReceived}`
// Update Email Events
| `${UpdateEmailEventOnReceived}`
| `${UpdateEmailEventEmit}`;
Loading

0 comments on commit 8afc7dc

Please sign in to comment.