From 46d240cd8b0bd531486d2dde47db39a211a7ddcf Mon Sep 17 00:00:00 2001 From: Kyle Peacock Date: Fri, 14 Oct 2022 13:59:20 -0700 Subject: [PATCH 1/3] bug: idlemanager does not get initialized until login --- package-lock.json | 25 ++++--- packages/auth-client/src/index.test.ts | 95 ++++++++++++++++++++++---- packages/auth-client/src/index.ts | 24 +++++-- packages/bls-verify/tsconfig.json | 2 +- packages/bls-verify/types/amcl.d.ts | 1 + 5 files changed, 114 insertions(+), 33 deletions(-) create mode 100644 packages/bls-verify/types/amcl.d.ts diff --git a/package-lock.json b/package-lock.json index b5577d1c1..6f895f741 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6322,8 +6322,13 @@ } }, "node_modules/amcl-js": { - "resolved": "packages/bls-verify/src/vendor/amcl-js", - "link": true + "version": "3.0.0", + "resolved": "file:packages/bls-verify/src/vendor/amcl-js-3.0.0.tgz", + "integrity": "sha512-YpgxYjeJ9vbfMU0YYzdA1qlmz3u4Dz02SFkcGH4wlrUQhzgdqr47ZYLU68Jf8nj9RRV6Fi1rG/5C4JlEiIy4Sg==", + "license": "Apache License 2.0", + "dependencies": { + "prompt": "^1.0.0" + } }, "node_modules/amp": { "version": "0.3.1", @@ -19799,14 +19804,7 @@ "version": "0.14.0", "license": "Apache-2.0", "dependencies": { - "amcl-js": "file:src/vendor/amcl-js" - } - }, - "packages/bls-verify/src/vendor/amcl-js": { - "version": "3.0.0", - "license": "Apache License 2.0", - "dependencies": { - "prompt": "^1.0.0" + "amcl-js": "file:src/vendor/amcl-js-3.0.0.tgz" } }, "packages/bls-verify/vendor/miracl": { @@ -21828,7 +21826,7 @@ "@dfinity/bls-verify": { "version": "file:packages/bls-verify", "requires": { - "amcl-js": "file:src/vendor/amcl-js" + "amcl-js": "file:src/vendor/amcl-js-3.0.0.tgz" } }, "@dfinity/candid": { @@ -22100,7 +22098,7 @@ "eslint": "^8.19.0", "eslint-plugin-jsdoc": "^39.3.3", "jest": "^28.1.2", - "js-sha256": "*", + "js-sha256": "^0.9.0", "text-encoding": "^0.7.0", "ts-jest": "^28.0.5", "ts-node": "^10.8.2", @@ -25453,7 +25451,8 @@ "requires": {} }, "amcl-js": { - "version": "file:packages/bls-verify/src/vendor/amcl-js", + "version": "file:src/vendor/amcl-js-3.0.0.tgz", + "integrity": "sha512-YpgxYjeJ9vbfMU0YYzdA1qlmz3u4Dz02SFkcGH4wlrUQhzgdqr47ZYLU68Jf8nj9RRV6Fi1rG/5C4JlEiIy4Sg==", "requires": { "prompt": "^1.0.0" } diff --git a/packages/auth-client/src/index.test.ts b/packages/auth-client/src/index.test.ts index bb7174c15..7c1525929 100644 --- a/packages/auth-client/src/index.test.ts +++ b/packages/auth-client/src/index.test.ts @@ -70,9 +70,14 @@ describe('Auth Client', () => { expect(await test.isAuthenticated()).toBe(false); expect(test.getIdentity().getPrincipal().isAnonymous()).toBe(true); }); - it('should initialize an idleManager', async () => { + it('should not initialize an idleManager if the user is not logged in', async () => { const test = await AuthClient.create(); + expect(test.idleManager).not.toBeDefined(); + }); + it('should initialize an idleManager if an identity is passed', async () => { + const test = await AuthClient.create({ identity: await Ed25519KeyIdentity.generate() }); expect(test.idleManager).toBeDefined(); + test.idleManager; //? }); it('should be able to invalidate an identity after going idle', async () => { // setup actor @@ -141,14 +146,6 @@ describe('Auth Client', () => { }); delete (window as any).location; (window as any).location = { reload: jest.fn(), fetch }; - const mockFetch: jest.Mock = jest.fn(); - - const canisterId = Principal.fromText('2chl6-4hpzw-vqaaa-aaaaa-c'); - const actorInterface = () => { - return IDL.Service({ - greet: IDL.Func([IDL.Text], [IDL.Text]), - }); - }; const storage: AuthClientStorage = { remove: jest.fn(), @@ -165,14 +162,14 @@ describe('Auth Client', () => { }); // Test login flow - await test.login({ identityProvider: 'http://localhost' }); + const onSuccess = jest.fn(); + test.login({ onSuccess }); + + idpMock.ready(); expect(storage.set).toBeCalled(); expect(storage.remove).not.toBeCalled(); - const httpAgent = new HttpAgent({ fetch: mockFetch, host: 'http://127.0.0.1:8000' }); - const actor = Actor.createActor(actorInterface, { canisterId, agent: httpAgent }); - // simulate user being inactive for 10 minutes jest.advanceTimersByTime(10 * 60 * 1000); @@ -218,7 +215,8 @@ describe('Auth Client', () => { }); // Test login flow - await test.login({ identityProvider: 'http://localhost' }); + await test.login(); + idpMock.ready(); expect(storage.set).toBeCalled(); expect(storage.remove).not.toBeCalled(); @@ -232,6 +230,24 @@ describe('Auth Client', () => { expect(window.location.reload).not.toBeCalled(); }); it('should not reload the page if a callback is provided', async () => { + setup({ + onAuthRequest: () => { + // Send a valid request. + idpMock.send({ + kind: 'authorize-client-success', + delegations: [ + { + delegation: { + pubkey: Uint8Array.from([]), + expiration: BigInt(0), + }, + signature: Uint8Array.from([]), + }, + ], + userPublicKey: Uint8Array.from([]), + }); + }, + }); delete (window as any).location; (window as any).location = { reload: jest.fn(), fetch }; const idleCb = jest.fn(); @@ -242,6 +258,9 @@ describe('Auth Client', () => { }, }); + test.login(); + idpMock.ready(); + // simulate user being inactive for 10 minutes jest.advanceTimersByTime(10 * 60 * 1000); @@ -325,6 +344,52 @@ describe('Auth Client', () => { jest.advanceTimersByTime(30 * 60 * 1000); expect(idleFn).not.toHaveBeenCalled(); }); + it('should not set up an idle timer if the client is not logged in', async () => { + setup({ + onAuthRequest: () => { + // Send a valid request. + idpMock.send({ + kind: 'authorize-client-success', + delegations: [ + { + delegation: { + pubkey: Uint8Array.from([]), + expiration: BigInt(0), + }, + signature: Uint8Array.from([]), + }, + ], + userPublicKey: Uint8Array.from([]), + }); + }, + }); + delete (window as any).location; + (window as any).location = { reload: jest.fn(), fetch }; + + const storage: AuthClientStorage = { + remove: jest.fn(), + get: jest.fn(), + set: jest.fn(), + }; + + const test = await AuthClient.create({ + storage, + idleOptions: { + idleTimeout: 1000, + }, + }); + + expect(storage.set).toBeCalled(); + expect(storage.remove).not.toBeCalled(); + + // simulate user being inactive for 10 minutes + jest.advanceTimersByTime(10 * 60 * 1000); + + // Storage should not be cleared + expect(storage.remove).not.toBeCalled(); + // Page should not be reloaded + expect(window.location.reload).not.toBeCalled(); + }); }); describe('IdbStorage', () => { @@ -527,7 +592,7 @@ describe('Auth Client login', () => { const client = await AuthClient.create(); const onSuccess = jest.fn(); - await client.login({ onSuccess: onSuccess }); + client.login({ onSuccess: onSuccess }); idpMock.ready(); diff --git a/packages/auth-client/src/index.ts b/packages/auth-client/src/index.ts index 1dd3049fa..8625e9cc1 100644 --- a/packages/auth-client/src/index.ts +++ b/packages/auth-client/src/index.ts @@ -252,9 +252,14 @@ export class AuthClient { key = null; } } - const idleManager = options.idleOptions?.disableIdle - ? undefined - : IdleManager.create(options.idleOptions); + let idleManager: IdleManager | undefined = undefined; + if (options.idleOptions?.disableIdle) { + idleManager = undefined; + } + // if there is a delegation chain or provided identity, setup idleManager + else if (chain || options.identity) { + idleManager = IdleManager.create(options.idleOptions); + } if (!key) { // Create a new key (whether or not one was in storage). @@ -270,7 +275,7 @@ export class AuthClient { private _key: SignIdentity, private _chain: DelegationChain | null, private _storage: AuthClientStorage, - public readonly idleManager: IdleManager | undefined, + public idleManager: IdleManager | undefined, private _createOptions: AuthClientCreateOptions | undefined, // A handle on the IdP window. private _idpWindow?: Window, @@ -317,6 +322,17 @@ export class AuthClient { this._identity = DelegationIdentity.fromDelegation(key, this._chain); this._idpWindow?.close(); + if (!this.idleManager) { + const idleOptions = this._createOptions?.idleOptions; + this.idleManager = IdleManager.create(idleOptions); + + if (!idleOptions?.onIdle && !idleOptions?.disableDefaultIdleCallback) { + this.idleManager?.registerCallback(() => { + this.logout(); + location.reload(); + }); + } + } onSuccess?.(); this._removeEventListener(); delete this._idpWindow; diff --git a/packages/bls-verify/tsconfig.json b/packages/bls-verify/tsconfig.json index f1fae3494..d6fb55d23 100644 --- a/packages/bls-verify/tsconfig.json +++ b/packages/bls-verify/tsconfig.json @@ -18,6 +18,6 @@ "strict": true, "target": "es2017" }, - "include": ["src/**/*"], + "include": ["src/**/*", "types/**/*", "amcl-js-3.0.0.tgz"], "references": [] } diff --git a/packages/bls-verify/types/amcl.d.ts b/packages/bls-verify/types/amcl.d.ts new file mode 100644 index 000000000..5a858db82 --- /dev/null +++ b/packages/bls-verify/types/amcl.d.ts @@ -0,0 +1 @@ +declare module 'amcl-js'; From a503dee5816cdd67978d987ebaafcd4757151b69 Mon Sep 17 00:00:00 2001 From: Kyle Peacock Date: Fri, 14 Oct 2022 14:00:32 -0700 Subject: [PATCH 2/3] changelog --- docs/generated/changelog.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/generated/changelog.html b/docs/generated/changelog.html index dd5d0a447..fb773acc3 100644 --- a/docs/generated/changelog.html +++ b/docs/generated/changelog.html @@ -13,6 +13,10 @@

Agent-JS Changelog

Version x.x.x

Version 0.14.0