Skip to content

Commit

Permalink
fix(@aws-amplify/auth): signout promise on OAuth flow (aws-amplify#6294)
Browse files Browse the repository at this point in the history
signOut promise race condition fix
  • Loading branch information
elorzafe authored Jul 15, 2020
1 parent 3206a4f commit 68af3ab
Show file tree
Hide file tree
Showing 2 changed files with 227 additions and 3 deletions.
219 changes: 218 additions & 1 deletion packages/auth/__tests__/hosted-ui.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUser', () => {
return CognitoUser;
});

import { Hub } from '@aws-amplify/core';
import { Hub, Credentials, StorageHelper } from '@aws-amplify/core';

const authOptionsWithOAuth: AuthOptions = {
userPoolId: 'awsUserPoolsId',
Expand Down Expand Up @@ -184,6 +184,7 @@ const session = new CognitoUserSession({
});

import { AuthClass as Auth } from '../src/Auth';
import { AuthOptions } from '../src/types';

describe('Hosted UI tests', () => {
beforeEach(() => {
Expand Down Expand Up @@ -245,4 +246,220 @@ describe('Hosted UI tests', () => {
});
}, 0);
});

test('globalSignOut hosted ui, timeout reject', async () => {
jest.spyOn(StorageHelper.prototype, 'getStorage').mockImplementation(() => {
return {
setItem() {},
getItem() {
return 'true';
},
removeItem() {},
};
});

const auth = new Auth(authOptionsWithOAuth);

const user = new CognitoUser({
Username: 'username',
Pool: userPool,
});

const spyonAuth = jest
.spyOn(Credentials, 'clear')
.mockImplementationOnce(() => {
return Promise.resolve();
});

const spyon = jest
.spyOn(CognitoUserPool.prototype, 'getCurrentUser')
.mockImplementationOnce(() => {
return user;
});

const spyon2 = jest
.spyOn(CognitoUser.prototype, 'globalSignOut')
.mockImplementation(({ onSuccess }) => {
onSuccess('success');
});

auth._oAuthHandler = {
signOut: () => {
// testing timeout
return new Promise(() => {});
},
};

expect.assertions(2);

try {
await auth.signOut({ global: true });
} catch (err) {
expect(err).toEqual('Signout timeout fail');
}

expect(spyon2).toBeCalled();

spyonAuth.mockClear();
spyon.mockClear();
spyon2.mockClear();
});

test('SignOut hosted ui, timeout reject', async () => {
jest.spyOn(StorageHelper.prototype, 'getStorage').mockImplementation(() => {
return {
setItem() {},
getItem() {
return 'true';
},
removeItem() {},
};
});

const auth = new Auth(authOptionsWithOAuth);

const user = new CognitoUser({
Username: 'username',
Pool: userPool,
});

const spyonAuth = jest
.spyOn(Credentials, 'clear')
.mockImplementationOnce(() => {
return Promise.resolve();
});

const spyon = jest
.spyOn(CognitoUserPool.prototype, 'getCurrentUser')
.mockImplementationOnce(() => {
return user;
});

auth._oAuthHandler = {
signOut: () => {
// testing timeout
return new Promise(() => {});
},
};

expect.assertions(1);

try {
await auth.signOut({ global: false });
} catch (err) {
expect(err).toEqual('Signout timeout fail');
}

spyonAuth.mockClear();
spyon.mockClear();
});
test('globalSignOut hosted ui, url opener', done => {
jest.spyOn(StorageHelper.prototype, 'getStorage').mockImplementation(() => {
return {
setItem() {},
getItem() {
return 'true';
},
removeItem() {},
};
});

const urlOpener = jest.fn(
(url: string, redirectUrl: string): Promise<any> => {
return new Promise(() => {
return new Promise(() => {
expect(url).toEqual(
'https://xxxxxxxxxxxx-xxxxxx-xxx.auth.us-west-2.amazoncognito.com/logout?client_id=awsUserPoolsWebClientId&logout_uri=http%3A%2F%2Flocalhost%3A4200%2F'
);

done();
});
});
}
);
const options = {
...authOptionsWithOAuth,
};
options.oauth.urlOpener = urlOpener;

const auth = new Auth(options);

const user = new CognitoUser({
Username: 'username',
Pool: userPool,
});

const spyonAuth = jest
.spyOn(Credentials, 'clear')
.mockImplementationOnce(() => {
return Promise.resolve();
});

const spyon = jest
.spyOn(CognitoUserPool.prototype, 'getCurrentUser')
.mockImplementationOnce(() => {
return user;
});

expect.assertions(1);

auth.signOut({ global: true });

spyonAuth.mockClear();
spyon.mockClear();
});

test('SignOut hosted ui, urlOpener', done => {
jest.spyOn(StorageHelper.prototype, 'getStorage').mockImplementation(() => {
return {
setItem() {},
getItem() {
return 'true';
},
removeItem() {},
};
});

const urlOpener = jest.fn(
(url: string): Promise<any> => {
return new Promise(() => {
expect(url).toEqual(
'https://xxxxxxxxxxxx-xxxxxx-xxx.auth.us-west-2.amazoncognito.com/logout?client_id=awsUserPoolsWebClientId&logout_uri=http%3A%2F%2Flocalhost%3A4200%2F'
);

done();
});
}
);
const options = {
...authOptionsWithOAuth,
};
options.oauth.urlOpener = urlOpener;

const auth = new Auth(options);

const user = new CognitoUser({
Username: 'username',
Pool: userPool,
});

const spyonAuth = jest
.spyOn(Credentials, 'clear')
.mockImplementationOnce(() => {
return Promise.resolve();
});

const spyon = jest
.spyOn(CognitoUserPool.prototype, 'getCurrentUser')
.mockImplementationOnce(() => {
return user;
});

expect.assertions(1);

auth.signOut({ global: true });

spyonAuth.mockClear();
spyon.mockClear();
});
});
11 changes: 9 additions & 2 deletions packages/auth/src/Auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1480,7 +1480,7 @@ export class AuthClass {
onSuccess: data => {
logger.debug('global sign out success');
if (isSignedInHostedUI) {
return res(this._oAuthHandler.signOut());
this.oAuthSignOutRedirectOrReject(rej);
} else {
return res();
}
Expand All @@ -1495,14 +1495,21 @@ export class AuthClass {
logger.debug('user sign out', user);
user.signOut();
if (isSignedInHostedUI) {
return res(this._oAuthHandler.signOut());
this.oAuthSignOutRedirectOrReject(rej);
} else {
return res();
}
}
});
}

private oAuthSignOutRedirectOrReject(reject: (reason?: any) => void) {
this._oAuthHandler.signOut(); // this method redirects url

// App should be redirected to another url otherwise it will reject
setTimeout(() => reject('Signout timeout fail'), 3000);
}

/**
* Sign out method
* @
Expand Down

0 comments on commit 68af3ab

Please sign in to comment.