-
Notifications
You must be signed in to change notification settings - Fork 267
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
Implement SyncStorageService using broadcast-channel #1197
Conversation
2bcc941
to
6fa57a4
Compare
Codecov Report
@@ Coverage Diff @@
## master #1197 +/- ##
==========================================
+ Coverage 93.41% 93.48% +0.06%
==========================================
Files 157 159 +2
Lines 4586 4681 +95
Branches 1058 1081 +23
==========================================
+ Hits 4284 4376 +92
- Misses 283 287 +4
+ Partials 19 18 -1
Continue to review full report at Codecov.
|
onLeader?: OnLeaderHandler; | ||
}; | ||
|
||
export class LeaderElectionService implements ServiceInterface { |
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.
What is the motivation for moving the tab elector logic to it's own service?
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.
I isolated election-related code from ServiceManager into a separate class for single responsibility, then saw it looks like a service.
fa79c0b
to
b4d565c
Compare
{ | ||
broadcastChannelName?: string; | ||
}; | ||
SyncStorageServiceOptions & LeaderElectionServiceOptions; |
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.
changing the name of broadcastChannelName
to electionChannelName
is a breaking change. Can you have the config respect both options until the next major release
lib/ServiceManager.ts
Outdated
@@ -43,14 +44,15 @@ export class ServiceManager implements ServiceManagerInterface { | |||
|
|||
// TODO: backwards compatibility, remove in next major version - OKTA-473815 | |||
const { autoRenew, autoRemove, syncStorage } = sdk.tokenManager.getOptions(); | |||
options.electionChannelName = options.broadcastChannelName; |
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.
this should be:
options.electionChannelName = options.electionChannelName || options.broadcastChannelName
to support passing electionChannelName
(as is being done in the test app)
const client = createAuth(options); | ||
const serviceManagerOptions = (client.serviceManager as any).options; | ||
expect(serviceManagerOptions.electionChannelName).toEqual('test-channel'); | ||
}); |
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.
Please add a test verifying that electionChannelName
option is supported
{ autoRenew, autoRemove, syncStorage }, | ||
{ | ||
electionChannelName: `${sdk.options.clientId}-election`, | ||
syncChannelName: `${sdk.options.clientId}-sync`, |
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.
please add a test for these default values in test/spec/ServiceManager
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.
also please add a test verifying syncChannelName
can be set
lib/types/TokenManager.ts
Outdated
|
||
export declare type AccessTokenCallback = (key: string, token: AccessToken) => void; | ||
export declare type IDTokenCallback = (key: string, token: IDToken) => void; | ||
export declare type RefreshTokenCallback = (key: string, token: RefreshToken) => void; | ||
|
||
// only add methods needed internally | ||
export interface TokenManagerInterface { | ||
on: (event: string, handler: TokenManagerErrorEventHandler | TokenManagerEventHandler, context?: object) => void; | ||
off: (event: string, handler?: TokenManagerErrorEventHandler | TokenManagerEventHandler) => void; | ||
on: (event: string, handler: TokenManagerAnyEventHandler, context?: object) => void; |
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.
Do you think we could handle this better with function overloads, like we are doing on SIW: https://github.com/okta/okta-signin-widget/blob/okta-signin-widget-6.3.3/src/types/api.ts#L10
Then it will be clear which type of event has which type of callback. As is, I'm not sure as a developer how to implement my handler function - it seems to just have one of 3 shapes, so I have to be prepared for any of the variations. Would be better I think to know which type of callback I should implement for a specific event.
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.
I have tested the functionality and it seems to work great! Just a few comments
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.
please add missing tests and fix logic with electionChannelName
07b4484
to
c609644
Compare
async .
498382d
to
d943b27
Compare
d943b27
to
45fc202
Compare
what happened here? |
@pzi This PR has been merged to master |
@denysoblohin-okta oh ok, I kinda expected the PR to be merged instead of closed :D thanks for that |
SyncStorageService
(service to sync authState cross multiple tabs) with usingbroadcast-channel
package instead of usingStorageEvent
LeaderElectionService
(leader election code was a part ofServiceManager
)Internal ref: OKTA-476153
Implementation notes:
Notes on broadcast-channel issues:
Failed to execute 'postMessage' on 'BroadcastChannel': Channel is closed
.See Auth JS 6.4.0 causes issues in Angular apps when testing #1174, Error on redirect from login okta-react#221, "Failed to execute 'postMessage' on 'BroadcastChannel'" error - React 18 okta-react#232
Cause: broadcast-channel's leader election tries to post internal message after closing the channel.
Workaround:
okta-auth-js/lib/services/LeaderElectionService.ts
Line 72 in 4f88408
https://github.com/pubkey/broadcast-channel/blob/773fbc3477d246e96f7566b7ac5ff3164042204d/src/util.js#L61
This leads to wrongly selecting 'node' method (based on fs sockets) for broadcast-channel during unit tests.
Causes tests flakiness.
For downstream SDKs causes error
No useable method found in ["native","idb","localstorage"]
, see Please provide unit test example okta-react#224Workaround:
okta-auth-js/test/support/jest/jest.setup.js
Lines 33 to 35 in 4f88408
Notes on IE11
https://github.com/okta/okta-auth-js/pull/1197/files#diff-a861a360629a870e92aba3e9458e109f96ad1c9512f8ff339b0a4072f7ce7d4c
(maybe replace multiple features with 'core-js/stable' ?)
And polyfill FormData: https://github.com/okta/okta-auth-js/pull/1197/files#diff-41fbe6af6c0cd49ffacdf69a8831d1a7327ea9d90849bd4bf95c27e6f6033ffcR210
localStorage
across tabs.Partial workaround is to set empty event handler for
window.onstorage
: https://github.com/okta/okta-auth-js/pull/1197/files#diff-4190d70999a6d0023de331545bbec8ba4167f9d6d71faa196061946d5f8822d9R129But it's not 100% working, sometimes you still get old value from LocalStorage.
So to fix this issue I've added a new type of event
EVENT_SET_STORAGE
in TokenManager (only for IE11) to update storage explicitly in other tabs.https://github.com/okta/okta-auth-js/pull/1197/files#diff-f18cf5684cf053b7ffbc2723f4983f1a26432ef9eade50cbfa00a74a7793dedbR280
https://github.com/okta/okta-auth-js/pull/1197/files#diff-b3d0b7dbba2f28b35e943fe28c40cc6b832b43b2986d5d14dd1ce9b7d164f3deR134
Other notes
localStorage
andcookie
storage.https://github.com/okta/okta-auth-js/pull/1197/files#diff-6d3aacb7a946c6ca690509a0cc75e9880e5ca6942803b7cdd4059a9ba9a31b2dR356
https://github.com/okta/okta-auth-js/pull/1197/files#diff-2128a2510aeabd863495fcf012a579ef877ad61544805295fff687cfce0728c4R55