-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[CP-9405] app authorization token #109
Open
bferenc
wants to merge
26
commits into
main
Choose a base branch
from
feat/appcheck
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 22 commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
fa8e628
feat: add dummy service for testing
bferenc 50a0585
feat: get appcheck token on service worker activation
bferenc 287c94e
feat: add firebase service
bferenc 9be7ae0
feat: add challenge helper utils
bferenc 3ec4e63
feat: add challenge solver utils
bferenc 9087120
Merge branch 'main' into feat/appcheck
bferenc dc4fdfc
feat: remove unsubscribe logic
bferenc 27f6057
feat: log fcm init error
bferenc 7efcbc7
feat: add app type and version headers
bferenc 3333758
feat: handle feature flag state
bferenc 4999dc4
chore: apply patches on build
bferenc 6d022a8
feat: improve base challenge
bferenc 16806f9
Merge branch 'main' into feat/appcheck
bferenc 4d32f3e
chore: fix type deps
bferenc 02a774d
test: add FirebaseService tests
bferenc 0aca297
test: add AppcheckService tests
bferenc 402f1eb
test: add getHashByAlgorithm tests
bferenc c0c3200
test: add registerForChallenge tests
bferenc 60acb57
test: add solveChallenge tests
bferenc 9762150
test: add verifyChallenge tests
bferenc 0821588
test: add basic challenge tests
bferenc b9c8dbe
chore: update allowed scripts config
bferenc 5bd5507
Merge branch 'main' into feat/appcheck
bferenc a872e40
chore: add reference to docs
bferenc 220306f
chore: lint
bferenc 0d5c181
fix: always create new subscription with correct options
bferenc File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
diff --git a/node_modules/@firebase/messaging/dist/esm/index.esm2017.js b/node_modules/@firebase/messaging/dist/esm/index.esm2017.js | ||
index 612f735..070ea56 100644 | ||
--- a/node_modules/@firebase/messaging/dist/esm/index.esm2017.js | ||
+++ b/node_modules/@firebase/messaging/dist/esm/index.esm2017.js | ||
@@ -565,7 +565,7 @@ async function getPushSubscription(swRegistration, vapidKey) { | ||
return subscription; | ||
} | ||
return swRegistration.pushManager.subscribe({ | ||
- userVisibleOnly: true, | ||
+ userVisibleOnly: false, | ||
// Chrome <= 75 doesn't support base64-encoded VAPID key. For backward compatibility, VAPID key | ||
// submitted to pushManager#subscribe must be of type Uint8Array. | ||
applicationServerKey: base64ToArray(vapidKey) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
187 changes: 187 additions & 0 deletions
187
src/background/services/appcheck/AppCheckService.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
import { | ||
AppCheck, | ||
CustomProvider, | ||
initializeAppCheck, | ||
setTokenAutoRefreshEnabled, | ||
} from 'firebase/app-check'; | ||
import { FirebaseService } from '../firebase/FirebaseService'; | ||
import { FcmMessageEvents, FirebaseEvents } from '../firebase/models'; | ||
import { | ||
AppCheckService, | ||
WAIT_FOR_CHALLENGE_ATTEMPT_COUNT, | ||
WAIT_FOR_CHALLENGE_DELAY_MS, | ||
} from './AppCheckService'; | ||
import registerForChallenge from './utils/registerForChallenge'; | ||
import { ChallengeTypes } from './models'; | ||
import { MessagePayload } from 'firebase/messaging/sw'; | ||
import solveChallenge from './utils/solveChallenge'; | ||
import verifyChallenge from './utils/verifyChallenge'; | ||
|
||
jest.mock('firebase/app-check'); | ||
jest.mock('./utils/registerForChallenge'); | ||
jest.mock('./utils/verifyChallenge'); | ||
jest.mock('./utils/solveChallenge'); | ||
|
||
describe('AppCheckService', () => { | ||
let appCheckService: AppCheckService; | ||
let firebaseService: FirebaseService; | ||
|
||
beforeEach(() => { | ||
jest.resetAllMocks(); | ||
|
||
firebaseService = { | ||
isFcmInitialized: true, | ||
getFirebaseApp: () => ({ name: 'test' }), | ||
getFcmToken: jest.fn().mockReturnValue('fcmToken'), | ||
addFcmMessageListener: jest.fn(), | ||
addFirebaseEventListener: jest.fn(), | ||
} as unknown as FirebaseService; | ||
|
||
appCheckService = new AppCheckService(firebaseService); | ||
appCheckService.activate(); | ||
}); | ||
|
||
it('subscribes for events on activation correctly', () => { | ||
expect(firebaseService.addFcmMessageListener).toHaveBeenCalledWith( | ||
FcmMessageEvents.ID_CHALLENGE, | ||
expect.any(Function) | ||
); | ||
|
||
expect(firebaseService.addFirebaseEventListener).toHaveBeenCalledTimes(2); | ||
expect(firebaseService.addFirebaseEventListener).toHaveBeenNthCalledWith( | ||
1, | ||
FirebaseEvents.FCM_INITIALIZED, | ||
expect.any(Function) | ||
); | ||
expect(firebaseService.addFirebaseEventListener).toHaveBeenNthCalledWith( | ||
2, | ||
FirebaseEvents.FCM_TERMINATED, | ||
expect.any(Function) | ||
); | ||
}); | ||
|
||
const appCheckMock = { app: { name: 'test' } } as AppCheck; | ||
|
||
beforeEach(() => { | ||
jest.useFakeTimers(); | ||
jest.mocked(initializeAppCheck).mockReturnValue(appCheckMock); | ||
|
||
// simulate FCM_INITIALIZED event | ||
jest.mocked(firebaseService.addFirebaseEventListener).mock.calls[0]?.[1](); | ||
}); | ||
|
||
afterEach(() => { | ||
jest.useRealTimers(); | ||
}); | ||
|
||
it('initializes appcheck correctly', () => { | ||
expect(setTokenAutoRefreshEnabled).not.toHaveBeenCalled(); | ||
expect(initializeAppCheck).toHaveBeenCalledWith( | ||
{ name: 'test' }, | ||
{ | ||
provider: expect.any(CustomProvider), | ||
isTokenAutoRefreshEnabled: true, | ||
} | ||
); | ||
|
||
// simulate FCM_INITIALIZED event (second time) | ||
jest.mocked(firebaseService.addFirebaseEventListener).mock.calls[0]?.[1](); | ||
|
||
expect(initializeAppCheck).toHaveBeenCalledTimes(1); | ||
expect(setTokenAutoRefreshEnabled).toHaveBeenCalledWith(appCheckMock, true); | ||
}); | ||
|
||
it('terminates appcheck correctly', () => { | ||
expect(setTokenAutoRefreshEnabled).not.toHaveBeenCalled(); | ||
expect(initializeAppCheck).toHaveBeenCalledWith( | ||
{ name: 'test' }, | ||
{ | ||
provider: expect.any(CustomProvider), | ||
isTokenAutoRefreshEnabled: true, | ||
} | ||
); | ||
|
||
// simulate FCM_TERMINATED event | ||
jest.mocked(firebaseService.addFirebaseEventListener).mock.calls[1]?.[1](); | ||
|
||
expect(setTokenAutoRefreshEnabled).toHaveBeenCalledWith( | ||
appCheckMock, | ||
false | ||
); | ||
}); | ||
|
||
describe('getToken', () => { | ||
it('throws when FCM is not initialized', async () => { | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
firebaseService.isFcmInitialized = false; | ||
await expect( | ||
jest.mocked(CustomProvider).mock.calls[0]?.[0].getToken() | ||
).rejects.toThrow('fcm is not initialized'); | ||
}); | ||
|
||
it('throws when FCM token is missing', async () => { | ||
jest.mocked(firebaseService.getFcmToken).mockReturnValueOnce(undefined); | ||
await expect( | ||
jest.mocked(CustomProvider).mock.calls[0]?.[0].getToken() | ||
).rejects.toThrow('fcm token is missing'); | ||
}); | ||
|
||
it('throws a timeout error when challenge is not received in time', async () => { | ||
jest | ||
.mocked(CustomProvider) | ||
.mock.calls[0]?.[0].getToken() | ||
.catch((err) => { | ||
expect(err).toBe('timeout'); | ||
}); | ||
|
||
for (let i = 0; i <= WAIT_FOR_CHALLENGE_ATTEMPT_COUNT; i++) { | ||
jest.advanceTimersByTime(WAIT_FOR_CHALLENGE_DELAY_MS); | ||
await Promise.resolve(); | ||
} | ||
}); | ||
|
||
it('generates a token correctly', async () => { | ||
jest.mocked(crypto.randomUUID).mockReturnValue('1-2-3-4-5'); | ||
jest.mocked(solveChallenge).mockResolvedValueOnce('solution'); | ||
jest | ||
.mocked(verifyChallenge) | ||
.mockResolvedValueOnce({ token: 'token', exp: 1234 }); | ||
|
||
const promise = jest.mocked(CustomProvider).mock.calls[0]?.[0].getToken(); | ||
|
||
// trigger ID_CHALLENGE event | ||
jest.mocked(firebaseService.addFcmMessageListener).mock.calls[0]?.[1]({ | ||
data: { | ||
requestId: crypto.randomUUID(), | ||
registrationId: 'registrationId', | ||
type: ChallengeTypes.BASIC, | ||
event: FcmMessageEvents.ID_CHALLENGE, | ||
details: '{}', | ||
}, | ||
} as unknown as MessagePayload); | ||
|
||
await Promise.resolve(); | ||
jest.advanceTimersByTime(1000); | ||
await Promise.resolve(); | ||
|
||
await expect(promise).resolves.toStrictEqual({ | ||
token: 'token', | ||
expireTimeMillis: 1234, | ||
}); | ||
|
||
expect(registerForChallenge).toHaveBeenCalledWith({ | ||
token: 'fcmToken', | ||
requestId: crypto.randomUUID(), | ||
}); | ||
expect(solveChallenge).toHaveBeenCalledWith({ | ||
type: ChallengeTypes.BASIC, | ||
challengeDetails: '{}', | ||
}); | ||
expect(verifyChallenge).toHaveBeenCalledWith({ | ||
registrationId: 'registrationId', | ||
solution: 'solution', | ||
}); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Chrome allows it since 121, but the SDK doesn't support it yet.