From d9e43f5a001414b59f10e81469013b4c8adcef7d Mon Sep 17 00:00:00 2001 From: undefined Date: Thu, 5 Dec 2024 03:04:27 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=94=84=20synced=20local=20'package.js?= =?UTF-8?q?on'=20with=20remote=20'package.json'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 004ef95..d7383af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@circle-fin/w3s-pw-web-sdk", - "version": "1.1.6", + "version": "1.1.7", "description": "Javascript/Typescript SDK for Circle Programmable Wallets", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", From 949d37b763dff7dd32d277fc501840f162aa7e0d Mon Sep 17 00:00:00 2001 From: undefined Date: Thu, 5 Dec 2024 03:04:27 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=94=84=20synced=20local=20'src/'=20wi?= =?UTF-8?q?th=20remote=20'src/'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.test.ts | 131 +++++++++++++++++++++++++++++++++++++++++++++- src/index.ts | 22 ++++++-- 2 files changed, 147 insertions(+), 6 deletions(-) diff --git a/src/index.test.ts b/src/index.test.ts index cafb1b6..cf3e988 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -16,14 +16,24 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ /** * @jest-environment jsdom */ +import * as firebaseAuth from 'firebase/auth' + +import { SocialLoginProvider } from './types' import { W3SSdk } from './' -import type { ChallengeResult, Error } from './types' +import type { ChallengeResult, Configs, Error } from './types' + +jest.mock('firebase/auth', () => ({ + ...jest.requireActual('firebase/auth'), + signInWithPopup: jest.fn(), + getAuth: jest.fn(), +})) describe('W3SSdk', () => { let sdk: W3SSdk @@ -101,3 +111,122 @@ describe('W3SSdk', () => { ) }) }) + +describe('W3SSdk > Apple OAuth', () => { + const localStorageMock = (() => { + let store: { [key: string]: string } = {} + + return { + getItem(key: string): string { + return store[key] || '' + }, + setItem(key: string, value: string): void { + store[key] = value + }, + removeItem(key: string): void { + delete store[key] + }, + clear(): void { + store = {} + }, + length: 0, + key(index: number): string | null { + return Object.keys(store)[index] || null + }, + } + })() + + Object.defineProperty(window, 'localStorage', { value: localStorageMock }) + + let sdk: W3SSdk + const configs: Configs = { + appSettings: { + appId: 'test-app-id', + }, + loginConfigs: { + deviceToken: 'device-token', + deviceEncryptionKey: 'device-encryption-key', + apple: { + apiKey: 'test-api-key', + authDomain: 'test-auth-domain', + projectId: 'test-project-id', + storageBucket: 'test-storage-bucket', + messagingSenderId: 'test-messaging-sender-id', + appId: 'test-app-id', + }, + }, + } + + beforeEach(() => { + jest.clearAllMocks() + window.localStorage.clear() + }) + + it('should perform Apple login successfully', async () => { + const onLoginComplete = jest.fn() + sdk = new W3SSdk(configs, onLoginComplete) + + const mockFirebaseApp = {} + Object.defineProperty(sdk, 'firebaseApp', { + get: jest.fn(() => mockFirebaseApp), + configurable: true, + }) + + // Mock signInWithPopup to resolve to a UserCredential + const userCredentialMock = { + user: { uid: 'test-uid' }, + credential: { idToken: 'test-id-token' }, + } + ;(firebaseAuth.signInWithPopup as jest.Mock).mockResolvedValueOnce( + userCredentialMock, + ) + + // Mock extractTokenFromResultAndSave to return true + const extractTokenSpy = jest + .spyOn(sdk as any, 'extractTokenFromResultAndSave') + .mockReturnValue(true) + + // Mock verifyTokenViaService + const verifyTokenSpy = jest + .spyOn(sdk as any, 'verifyTokenViaService') + .mockImplementation(() => {}) + + await sdk.performLogin(SocialLoginProvider.APPLE) + + expect(firebaseAuth.signInWithPopup).toHaveBeenCalled() + expect(extractTokenSpy).toHaveBeenCalledWith(userCredentialMock) + expect(verifyTokenSpy).toHaveBeenCalled() + + expect(window.localStorage.getItem('socialLoginProvider')).toBe('') + }) + + it('should handle signInWithPopup error during Apple login', async () => { + const onLoginComplete = jest.fn() + sdk = new W3SSdk(configs, onLoginComplete) + + // Simulate firebaseApp being initialized + const mockFirebaseApp = {} + Object.defineProperty(sdk, 'firebaseApp', { + get: jest.fn(() => mockFirebaseApp), + configurable: true, + }) + + // Mock getAuth + const mockAuth = { getProvider: jest.fn() } + ;(firebaseAuth.getAuth as jest.Mock).mockReturnValue(mockAuth) + + // Mock signInWithPopup to reject + const error = new Error('sign in error') + ;(firebaseAuth.signInWithPopup as jest.Mock).mockRejectedValueOnce(error) + + // Mock handleLoginFailure + const handleLoginFailureSpy = jest + .spyOn(sdk as any, 'handleLoginFailure') + .mockImplementation(() => {}) + + await sdk.performLogin(SocialLoginProvider.APPLE) + + expect(firebaseAuth.signInWithPopup).toHaveBeenCalled() + expect(handleLoginFailureSpy).toHaveBeenCalled() + }) +}) diff --git a/src/index.ts b/src/index.ts index 3b1ba36..e4e3b53 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,7 +17,7 @@ import { OAuthProvider, getAuth, getRedirectResult, - signInWithRedirect, + signInWithPopup, } from 'firebase/auth' import { decode } from 'jsonwebtoken' import { v4 as uuidv4 } from 'uuid' @@ -168,13 +168,13 @@ export class W3SSdk { * Performs social login. * @param provider - Social login provider. */ - performLogin(provider: SocialLoginProvider): void { + async performLogin(provider: SocialLoginProvider): Promise { if (provider === SocialLoginProvider.GOOGLE) { this.performGoogleLogin() } else if (provider === SocialLoginProvider.FACEBOOK) { this.performFacebookLogin() } else if (provider === SocialLoginProvider.APPLE) { - this.performAppleLogin() + await this.performAppleLogin() } else { void this.onLoginComplete?.( { @@ -381,7 +381,7 @@ export class W3SSdk { }, 1000 * 10) } - private performAppleLogin() { + private async performAppleLogin() { if (!this.firebaseApp) { void this.onLoginComplete?.( { @@ -398,7 +398,19 @@ export class W3SSdk { const provider = new OAuthProvider('apple.com') const auth = getAuth(this.firebaseApp) - void signInWithRedirect(auth, provider) + try { + const cred = await signInWithPopup(auth, provider) + + if (!this.extractTokenFromResultAndSave(cred)) { + return + } + + // Send the token to the verification service and reset the social login provider + this.verifyTokenViaService() + this.window.localStorage.setItem('socialLoginProvider', '') + } catch (error) { + this.handleLoginFailure() + } } private performFacebookLogin() {