From fc0294b6cc903be5e95e7e707eae76dfb36438f2 Mon Sep 17 00:00:00 2001 From: Dustin Noyes Date: Tue, 9 Jul 2019 08:27:55 -0700 Subject: [PATCH] query string parsing and customChallenge Cognito response case (#3608) * query string parsing and customChallenge Cognito response case * checking for metadata from cognito response * unit tests for react and vue --- .../sign-in.component.core.ts | 23 ++++++-- .../username-field.component.core.ts | 16 +++++- .../__tests__/Auth/SignIn-test.js | 52 ++++++++++++++++++- .../aws-amplify-react/src/Auth/AuthPiece.jsx | 6 +++ .../aws-amplify-react/src/Auth/SignIn.jsx | 6 +++ .../__mocks__/Amplify.mocks.js | 5 +- .../aws-amplify-vue/__tests__/SignIn.test.js | 17 ++++++ .../src/components/authenticator/SignIn.vue | 6 +++ .../authenticator/UsernameField.vue | 8 +++ 9 files changed, 131 insertions(+), 8 deletions(-) diff --git a/packages/aws-amplify-angular/src/components/authenticator/sign-in-component/sign-in.component.core.ts b/packages/aws-amplify-angular/src/components/authenticator/sign-in-component/sign-in.component.core.ts index 3128e53ab67..b19b2f31194 100644 --- a/packages/aws-amplify-angular/src/components/authenticator/sign-in-component/sign-in.component.core.ts +++ b/packages/aws-amplify-angular/src/components/authenticator/sign-in-component/sign-in.component.core.ts @@ -32,7 +32,9 @@ const template = ` (usernameFieldChanged)="onUsernameFieldChanged($event)" >
- + - {{ this.amplifyService.i18n().get('Forgot Password?') }} - + {{ this.amplifyService.i18n().get('Forgot Password?') }} + {{ this.amplifyService.i18n().get('Reset your password') }} + >{{ this.amplifyService.i18n().get('Reset your password') }} +
@@ -121,7 +125,10 @@ export class SignInComponentCore implements OnInit { this._show = includes(['signIn', 'signedOut', 'signedUp'], authState.state); this.username = authState.user? authState.user.username || '' : ''; this.email = authState.user? authState.user.email || '' : ''; - this.country_code = authState.user && authState.user.country_code? authState.user.country_code : this.country_code; + this.country_code = authState.user && + authState.user.country_code ? + authState.user.country_code : + this.country_code; this.local_phone_number = authState.user? authState.user.local_phone_number || '' : ''; } @@ -156,6 +163,12 @@ export class SignInComponentCore implements OnInit { this.amplifyService.setAuthState({ state: 'confirmSignIn', user }); } else if (user['challengeName'] === 'NEW_PASSWORD_REQUIRED') { this.amplifyService.setAuthState({ state: 'requireNewPassword', user }); + } else if ( + user['challengeName'] === 'CUSTOM_CHALLENGE' && + user.challengeParam && + user.challengeParam.trigger === 'true' + ) { + this.amplifyService.setAuthState({ state: 'customConfirmSignIn', user }); } else { this.amplifyService.setAuthState({ state: 'signedIn', user }); } diff --git a/packages/aws-amplify-angular/src/components/authenticator/username-field-component/username-field.component.core.ts b/packages/aws-amplify-angular/src/components/authenticator/username-field-component/username-field.component.core.ts index 9aaa019791e..e00c182aaf5 100644 --- a/packages/aws-amplify-angular/src/components/authenticator/username-field-component/username-field.component.core.ts +++ b/packages/aws-amplify-angular/src/components/authenticator/username-field-component/username-field.component.core.ts @@ -43,6 +43,7 @@ const template = ` #usernameField class="amplify-form-input" type="text" + value="{{this.username}}" placeholder="{{ this.amplifyService.i18n().get(this.getPlaceholder()) }}" (keyup)="setUsername($event.target.value)" data-test="${auth.genericAttrs.usernameInput}" @@ -58,6 +59,7 @@ const template = ` export class UsernameFieldComponentCore implements OnInit { _usernameAttributes : string = UsernameAttributes.USERNAME; _placeholder : string = ''; + username: string; constructor(@Inject(AmplifyService) protected amplifyService: AmplifyService) { this.onPhoneFieldChanged = this.onPhoneFieldChanged.bind(this); @@ -82,7 +84,19 @@ export class UsernameFieldComponentCore implements OnInit { @Output() usernameFieldChanged: EventEmitter = new EventEmitter(); - ngOnInit() {} + ngOnInit() { + if (window && + window.location && + window.location.search && + this._usernameAttributes !== 'email' && + this._usernameAttributes !== 'phone_number' + ) { + const searchParams = new URLSearchParams(window.location.search); + const username = searchParams ? searchParams.get('username') : undefined; + this.setUsername(username); + this.username = username; + } + } ngOnDestroy() {} diff --git a/packages/aws-amplify-react/__tests__/Auth/SignIn-test.js b/packages/aws-amplify-react/__tests__/Auth/SignIn-test.js index 033168269fb..3aa5f513709 100644 --- a/packages/aws-amplify-react/__tests__/Auth/SignIn-test.js +++ b/packages/aws-amplify-react/__tests__/Auth/SignIn-test.js @@ -8,7 +8,8 @@ import { Header, Footer, Input, Button } from '../../src/Amplify-UI/Amplify-UI-C const acceptedStates = [ 'signIn', 'signedUp', - 'signedOut' + 'signedOut', + 'customConfirmSignIn' ]; const deniedStates = [ @@ -99,6 +100,55 @@ describe('SignIn', () => { spyon_changeState.mockClear(); }); + test('when clicking signIn and trigger-based custom auth challenge present required', async () => { + const wrapper = shallow(); + wrapper.setProps({ + authState: 'signIn', + theme: AmplifyTheme + }); + + const spyon = jest.spyOn(Auth, 'signIn') + .mockImplementationOnce((user, password) => { + return new Promise((res, rej) => { + res({ + challengeName: 'CUSTOM_CHALLENGE', + challengeParam: { trigger: 'true' } + }); + }); + }); + + const spyon_changeState = jest.spyOn(wrapper.instance(), 'changeState'); + + const event_username = { + target: { + name: 'username', + value: 'user1' + } + }; + const event_password = { + target: { + name: 'password', + value: 'abc' + } + }; + + wrapper.find(Input).at(0).simulate('change', event_username); + wrapper.find(Input).at(1).simulate('change', event_password); + wrapper.find('form').at(0).simulate('submit', fakeEvent); + + await Promise.resolve(); + + expect(spyon.mock.calls.length).toBe(1); + expect(spyon.mock.calls[0][0]).toBe(event_username.target.value); + expect(spyon.mock.calls[0][1]).toBe(event_password.target.value); + + expect(spyon_changeState).toBeCalled(); + expect(spyon_changeState.mock.calls[0][0]).toBe('customConfirmSignIn'); + + spyon.mockClear(); + spyon_changeState.mockClear(); + }); + test('when clicking signIn and user session null, need verification of email and phone', async () => { const wrapper = shallow(); wrapper.setProps({ diff --git a/packages/aws-amplify-react/src/Auth/AuthPiece.jsx b/packages/aws-amplify-react/src/Auth/AuthPiece.jsx index 82e06fa783c..313e65659b4 100644 --- a/packages/aws-amplify-react/src/Auth/AuthPiece.jsx +++ b/packages/aws-amplify-react/src/Auth/AuthPiece.jsx @@ -87,10 +87,16 @@ export default class AuthPiece extends React.Component { ); } else { + let value; + if (window && window.location && window.search) { + const searchParams = new URLSearchParams(window.location.search); + value = searchParams ? searchParams.get('username') : undefined + } return ( {I18n.get(this.getUsernameLabel())} * Promise.resolve({})), setPreferredMFA: jest.fn(() => Promise.resolve({})), setupTOTP: jest.fn(() => Promise.resolve('gibberish')), - signIn: jest.fn(() => Promise.resolve({})), + signIn: jest.fn(() => Promise.resolve({ + challengeName: 'CUSTOM_CHALLENGE', + challengeParam: { trigger: 'true' }, + })), signOut: jest.fn(() => Promise.resolve({})), signUp: jest.fn(() => Promise.resolve({})), verifiedContact: jest.fn(() => Promise.resolve({})), diff --git a/packages/aws-amplify-vue/__tests__/SignIn.test.js b/packages/aws-amplify-vue/__tests__/SignIn.test.js index 38704f12bd9..689e81c0046 100644 --- a/packages/aws-amplify-vue/__tests__/SignIn.test.js +++ b/packages/aws-amplify-vue/__tests__/SignIn.test.js @@ -8,6 +8,7 @@ import AmplifyPlugin from '../src/plugins/AmplifyPlugin'; import * as AmplifyMocks from '../__mocks__/Amplify.mocks'; /* eslint-enable */ + Vue.use(AmplifyPlugin, AmplifyMocks); describe('SignIn', () => { @@ -69,10 +70,26 @@ describe('SignIn', () => { expect(wrapper.vm.options.header).toEqual('i18n Sign in to your account'); expect(wrapper.vm.options.username).toEqual(''); }); + + it('...have default options', () => { + expect(wrapper.vm.options.header).toEqual('i18n Sign in to your account'); + expect(wrapper.vm.options.username).toEqual(''); + }); + it('...should call Auth.signIn when signIn function is called', () => { wrapper.vm.signIn(); expect(wrapper.vm.$Amplify.Auth.signIn).toHaveBeenCalledTimes(1); }); + it('...should emit customConfirmSignIn state when signIn function is called and response indicates a custom challenge', async () => { + let customTestState = 0; + AmplifyEventBus.$on('authState', (val) => { + if (val === 'customConfirmSignIn') { + customTestState = 3; + } + }); + await wrapper.vm.signIn(); + expect(customTestState).toEqual(3); + }); it('...should emit authState when forgot method called', () => { testState = 0; AmplifyEventBus.$on('authState', (val) => { diff --git a/packages/aws-amplify-vue/src/components/authenticator/SignIn.vue b/packages/aws-amplify-vue/src/components/authenticator/SignIn.vue index 8e2e29d8682..98e5babd323 100644 --- a/packages/aws-amplify-vue/src/components/authenticator/SignIn.vue +++ b/packages/aws-amplify-vue/src/components/authenticator/SignIn.vue @@ -98,6 +98,12 @@ export default { } else if (data.challengeName === 'MFA_SETUP') { AmplifyEventBus.$emit('localUser', data); return AmplifyEventBus.$emit('authState', 'setMfa'); + } else if (data.challengeName === 'CUSTOM_CHALLENGE' && + data.challengeParam && + data.challengeParam.trigger === 'true' + ) { + AmplifyEventBus.$emit('localUser', data); + return AmplifyEventBus.$emit('authState', 'customConfirmSignIn') } else { return AmplifyEventBus.$emit('authState', 'signedIn') } diff --git a/packages/aws-amplify-vue/src/components/authenticator/UsernameField.vue b/packages/aws-amplify-vue/src/components/authenticator/UsernameField.vue index 2fc9e9b98ad..f5a671609a7 100644 --- a/packages/aws-amplify-vue/src/components/authenticator/UsernameField.vue +++ b/packages/aws-amplify-vue/src/components/authenticator/UsernameField.vue @@ -67,6 +67,14 @@ export default { auth, } }, + mounted: function() { + if (window && window.location && window.location.search) { + const searchParams = new URLSearchParams(window.location.search); + const usernameParam = searchParams ? searchParams.get('username') : this.username; + this.username = usernameParam; + this.$emit('username-field-changed', {usernameField: 'username', username: usernameParam}); + } + }, computed: { shouldRenderEmailField() { return this.usernameAttributes === 'email';