diff --git a/src/CoreManager.ts b/src/CoreManager.ts index 03e2b6002..4871b88a1 100644 --- a/src/CoreManager.ts +++ b/src/CoreManager.ts @@ -197,7 +197,7 @@ export interface UserController { loginAs: (user: ParseUser, userId: string) => Promise; become: (user: ParseUser, options?: RequestOptions) => Promise; hydrate: (user: ParseUser, userJSON: AttributeMap) => Promise; - logOut: (options?: RequestOptions) => Promise; + logOut: (options?: RequestOptions & { clearSession?: boolean }) => Promise; me: (user: ParseUser, options?: RequestOptions) => Promise; requestPasswordReset: (email: string, options?: RequestOptions) => Promise; updateUserOnDisk: (user: ParseUser) => Promise; diff --git a/src/ParseUser.ts b/src/ParseUser.ts index 5f3b39b76..e636cf687 100644 --- a/src/ParseUser.ts +++ b/src/ParseUser.ts @@ -812,11 +812,13 @@ class ParseUser extends ParseObject { * current will return null. * * @param {object} options + * @param {boolean} [options.clearSession] If true, the session token will be + * removed from the user object when the session token is invalid. * @static * @returns {Promise} A promise that is resolved when the session is * destroyed on the server. */ - static logOut(options?: RequestOptions): Promise { + static logOut(options?: RequestOptions & { clearSession?: boolean }): Promise { const controller = CoreManager.getUserController(); return controller.logOut(options); } @@ -1205,10 +1207,17 @@ const DefaultController = { }); }, - logOut(options?: RequestOptions): Promise { + logOut(options?: RequestOptions & { clearSession?: boolean }): Promise { const RESTController = CoreManager.getRESTController(); + const promiseCatch = e => { + console.log(e, options); + if (e.code === ParseError.INVALID_SESSION_TOKEN && options?.clearSession) { + return; + } + throw e; + }; if (options?.sessionToken) { - return RESTController.request('POST', 'logout', {}, options); + return RESTController.request('POST', 'logout', {}, options).catch(promiseCatch); } return DefaultController.currentUserAsync().then(currentUser => { const path = Storage.generatePath(CURRENT_USER_KEY); @@ -1216,9 +1225,11 @@ const DefaultController = { if (currentUser !== null) { const currentSession = currentUser.getSessionToken(); if (currentSession && isRevocableSession(currentSession)) { - promise = promise.then(() => { - return RESTController.request('POST', 'logout', {}, { sessionToken: currentSession }); - }); + promise = promise + .then(() => { + return RESTController.request('POST', 'logout', {}, { sessionToken: currentSession }); + }) + .catch(promiseCatch); } currentUser._logOutWithAll(); currentUser._finishFetch({ sessionToken: undefined }); diff --git a/src/__tests__/ParseUser-test.js b/src/__tests__/ParseUser-test.js index 2a0a7b6bf..fd81ae7ae 100644 --- a/src/__tests__/ParseUser-test.js +++ b/src/__tests__/ParseUser-test.js @@ -1042,6 +1042,62 @@ describe('ParseUser', () => { }); }); + it('can logout user with clear session', async () => { + ParseUser.disableUnsafeCurrentUser(); + ParseUser._clearCache(); + Storage._clear(); + const RESTController = { + request() { + const error = new ParseError(ParseError.INVALID_SESSION_TOKEN, 'Invalid session token.'); + return Promise.reject(error); + }, + ajax() {}, + }; + jest.spyOn(RESTController, 'request'); + CoreManager.setRESTController(RESTController); + + await ParseUser.logOut({ clearSession: true }); + }); + + it('can logout user with clear session and session token', async () => { + ParseUser.disableUnsafeCurrentUser(); + ParseUser._clearCache(); + Storage._clear(); + const RESTController = { + request() { + const error = new ParseError(ParseError.INVALID_SESSION_TOKEN, 'Invalid session token.'); + return Promise.reject(error); + }, + ajax() {}, + }; + jest.spyOn(RESTController, 'request'); + CoreManager.setRESTController(RESTController); + + await ParseUser.logOut({ sessionToken: '1234', clearSession: true }); + }); + + it('cannot logout user with invalid session', async () => { + expect.assertions(2); + ParseUser.disableUnsafeCurrentUser(); + ParseUser._clearCache(); + Storage._clear(); + const RESTController = { + request() { + const error = new ParseError(ParseError.INVALID_SESSION_TOKEN, 'Invalid session token.'); + return Promise.reject(error); + }, + ajax() {}, + }; + jest.spyOn(RESTController, 'request'); + CoreManager.setRESTController(RESTController); + try { + await ParseUser.logOut({ sessionToken: '1234', clearSession: false }); + } catch (e) { + expect(e.code).toBe(ParseError.INVALID_SESSION_TOKEN); + expect(e.message).toBe('Invalid session token.'); + } + }); + it('can retreive a user with sessionToken (me)', async () => { ParseUser.disableUnsafeCurrentUser(); ParseUser._clearCache(); diff --git a/types/CoreManager.d.ts b/types/CoreManager.d.ts index c01c1d4a9..d66014350 100644 --- a/types/CoreManager.d.ts +++ b/types/CoreManager.d.ts @@ -178,7 +178,9 @@ export interface UserController { loginAs: (user: ParseUser, userId: string) => Promise; become: (user: ParseUser, options?: RequestOptions) => Promise; hydrate: (user: ParseUser, userJSON: AttributeMap) => Promise; - logOut: (options?: RequestOptions) => Promise; + logOut: (options?: RequestOptions & { + clearSession?: boolean; + }) => Promise; me: (user: ParseUser, options?: RequestOptions) => Promise; requestPasswordReset: (email: string, options?: RequestOptions) => Promise; updateUserOnDisk: (user: ParseUser) => Promise; diff --git a/types/ParseUser.d.ts b/types/ParseUser.d.ts index 8e08dc2a5..88fd74a6e 100644 --- a/types/ParseUser.d.ts +++ b/types/ParseUser.d.ts @@ -392,11 +392,15 @@ declare class ParseUser extends ParseObjectcurrent will return null. * * @param {object} options + * @param {boolean} [options.clearSession] If true, the session token will be + * removed from the user object when the session token is invalid. * @static * @returns {Promise} A promise that is resolved when the session is * destroyed on the server. */ - static logOut(options?: RequestOptions): Promise; + static logOut(options?: RequestOptions & { + clearSession?: boolean; + }): Promise; /** * Requests a password reset email to be sent to the specified email address * associated with the user account. This email allows the user to securely