Skip to content

Commit 766bf30

Browse files
wlee221slaymance
andauthored
fix(authenticator): Move autoSignIn logic out of signUp actor (#1910)
* Remove `Auth.signIn` from `confirmSignUp` service This should fail because we aren't setting intents yet. * Implement "autoSignIn" redirect intent * Fix incorrect json keys * Remove verification step in autoSignIn * Remove unused var * Add autoSignIn route * Add test for sms-mfa redirect after autosignin * Revert "Fix incorrect json keys" This reverts commit f0d954e. * Create eight-insects-mix.md * Create famous-vans-smile.md * Edit wording * Update eight-insects-mix.md * Update packages/ui/src/machines/authenticator/actors/signIn.ts Co-authored-by: Shane Laymance <[email protected]> Co-authored-by: Shane Laymance <[email protected]>
1 parent bb193a4 commit 766bf30

File tree

9 files changed

+137
-30
lines changed

9 files changed

+137
-30
lines changed

.changeset/eight-insects-mix.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@aws-amplify/ui-react": patch
3+
"@aws-amplify/ui": patch
4+
---
5+
6+
refactor(internal): Move `autoSignIn` logic out of `signUp` actor

.changeset/famous-vans-smile.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@aws-amplify/ui-react": patch
3+
"@aws-amplify/ui": patch
4+
---
5+
6+
On userpools with sms mfa required, authenticator will now automatically redirect user to sms mfa page after successful sign up.
7+
8+
Previously, end users needed to sign in again to go to the sms mfa page ([#1660](https://github.com/aws-amplify/amplify-ui/issues/1660)).
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"challengeName": "SMS_MFA",
3+
"challengeParameters": {
4+
"CODE_DELIVERY_DELIVERY_MEDIUM": "SMS",
5+
"CODE_DELIVERY_DESTINATION": "+*******0839"
6+
},
7+
"session": "••••••"
8+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Feature: Sign up with SMS MFA
2+
3+
If your backend has SMS MFA required, Authenticator will redirect end users to
4+
SMS confirmation screen when they successfully sign up.
5+
6+
Background:
7+
Given I'm running the example "ui/components/authenticator/sign-in-sms-mfa"
8+
And I intercept '{ "headers": { "X-Amz-Target": "AWSCognitoIdentityProviderService.SignUp" } }' with fixture "sign-up-with-phone"
9+
When I click the "Create Account" tab
10+
11+
@angular @react @vue
12+
Scenario: Successful sign up redirects user to sms mfa route
13+
When I select my country code with status "UNCONFIRMED"
14+
And I type my "phone number" with status "UNCONFIRMED"
15+
And I type my password
16+
And I confirm my password
17+
And I type my "email" with status "UNCONFIRMED"
18+
And I click the "Create Account" button
19+
Then I see "Confirmation Code"
20+
And I type a valid confirmation code
21+
And I intercept '{ "headers": { "X-Amz-Target": "AWSCognitoIdentityProviderService.ConfirmSignUp" } }' with fixture "confirm-sign-up-with-email"
22+
# Mocking these two calls is much easier than intercepting 6+ network calls with tokens that are validated & expire within the hour
23+
And I mock 'Amplify.Auth.signIn' with fixture "Auth.signIn-sms-mfa"
24+
And I mock 'Amplify.Auth.currentAuthenticatedUser' with fixture "Auth.currentAuthenticatedUser-verified-email"
25+
And I click the "Confirm" button
26+
Then I see "Confirm SMS Code"
27+

packages/react/src/components/Authenticator/Router/Router.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const getRouteComponent = (route: string) => {
1616
case 'authenticated':
1717
case 'idle':
1818
case 'setup':
19+
case 'autoSignIn':
1920
return () => null;
2021
case 'confirmSignUp':
2122
return ConfirmSignUp;

packages/ui/src/helpers/authenticator/facade.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ export const getServiceContextFacade = (state: AuthMachineState) => {
7373
return 'signOut';
7474
case state.matches('authenticated'):
7575
return 'authenticated';
76+
case actorState?.matches('autoSignIn'):
77+
return 'autoSignIn';
7678
case actorState?.matches('confirmSignUp'):
7779
return 'confirmSignUp';
7880
case actorState?.matches('confirmSignIn'):

packages/ui/src/machines/authenticator/actors/signIn.ts

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ export function signInActor({ services }: SignInMachineOptions) {
4141
id: 'signInActor',
4242
states: {
4343
init: {
44-
always: [{ target: 'signIn' }],
44+
always: [
45+
{ target: 'autoSignIn', cond: 'shouldAutoSignIn' },
46+
{ target: 'signIn' },
47+
],
4548
},
4649
signIn: {
4750
initial: 'edit',
@@ -141,6 +144,62 @@ export function signInActor({ services }: SignInMachineOptions) {
141144
rejected: { always: '#signInActor.rejected' },
142145
},
143146
},
147+
autoSignIn: {
148+
initial: 'submit',
149+
states: {
150+
submit: {
151+
tags: ['pending'],
152+
entry: ['clearError', 'sendUpdate'],
153+
invoke: {
154+
src: 'signIn',
155+
onDone: [
156+
{
157+
cond: 'shouldSetupTOTP',
158+
actions: ['setUser', 'setChallengeName'],
159+
target: '#signInActor.setupTOTP',
160+
},
161+
{
162+
cond: 'shouldConfirmSignIn',
163+
actions: ['setUser', 'setChallengeName'],
164+
target: '#signInActor.confirmSignIn',
165+
},
166+
{
167+
cond: 'shouldForceChangePassword',
168+
actions: [
169+
'setUser',
170+
'setChallengeName',
171+
'setRequiredAttributes',
172+
],
173+
target: '#signInActor.forceNewPassword',
174+
},
175+
{
176+
actions: 'setUser',
177+
target: '#signInActor.resolved',
178+
},
179+
],
180+
onError: [
181+
{
182+
cond: 'shouldRedirectToConfirmSignUp',
183+
actions: ['setCredentials', 'setConfirmSignUpIntent'],
184+
target: '#signInActor.rejected',
185+
},
186+
{
187+
cond: 'shouldRedirectToConfirmResetPassword',
188+
actions: [
189+
'setUsernameAuthAttributes',
190+
'setConfirmResetPasswordIntent',
191+
],
192+
target: '#signInActor.rejected',
193+
},
194+
{
195+
actions: 'setRemoteError',
196+
target: '#signInActor.signIn',
197+
},
198+
],
199+
},
200+
},
201+
},
202+
},
144203
confirmSignIn: {
145204
initial: 'edit',
146205
exit: ['clearFormValues', 'clearError', 'clearTouched'],
@@ -414,6 +473,9 @@ export function signInActor({ services }: SignInMachineOptions) {
414473

415474
return validChallengeNames.includes(challengeName);
416475
},
476+
shouldAutoSignIn: (context) => {
477+
return context?.intent === 'autoSignIn';
478+
},
417479
shouldRedirectToConfirmSignUp: (_, event): boolean => {
418480
return event.data.code === 'UserNotConfirmedException';
419481
},
@@ -438,7 +500,15 @@ export function signInActor({ services }: SignInMachineOptions) {
438500
},
439501
services: {
440502
async signIn(context) {
441-
const { username, password } = context.formValues;
503+
/**
504+
* `authAttributes` are any username/password combo we remembered in
505+
* memory. This is used in autoSignIn flow usually to pass username/pw
506+
* from `confirmSignUp`.
507+
*/
508+
const { authAttributes = {}, formValues = {} } = context;
509+
510+
const credentials = { ...authAttributes, ...formValues };
511+
const { username, password } = credentials;
442512

443513
return await services.handleSignIn({
444514
username,

packages/ui/src/machines/authenticator/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,9 @@ export function createAuthenticatorMachine() {
133133
on: {
134134
SIGN_IN: 'signIn',
135135
'done.invoke.signUpActor': {
136-
target: 'signUp.autoSignIn',
136+
target: '#authenticator.signIn',
137137
actions: 'setActorDoneData',
138+
cond: 'shouldAutoSignIn',
138139
},
139140
},
140141
},
@@ -293,7 +294,7 @@ export function createAuthenticatorMachine() {
293294
actorRef: (context, _) => {
294295
const { services } = context;
295296
const actor = signInActor({ services }).withContext({
296-
authAttributes: context.actorDoneData?.authAttributes,
297+
authAttributes: context.actorDoneData?.authAttributes ?? {},
297298
user: context.user,
298299
intent: context.actorDoneData?.intent,
299300
country_code: DEFAULT_COUNTRY_CODE,
@@ -373,6 +374,7 @@ export function createAuthenticatorMachine() {
373374
event.data?.intent === 'confirmSignUp',
374375
shouldRedirectToResetPassword: (_, event) =>
375376
event.data?.intent === 'confirmPasswordReset',
377+
shouldAutoSignIn: (_, event) => event.data?.intent === 'autoSignIn',
376378
shouldSetup: (context) => context.hasSetup === false,
377379
// other context guards
378380
hasActor: (context) => !!context.actorRef,

packages/ui/src/machines/authenticator/signUp.ts

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Auth } from 'aws-amplify';
22
import get from 'lodash/get';
33
import pickBy from 'lodash/pickBy';
4-
import { createMachine, sendUpdate } from 'xstate';
4+
import { assign, createMachine, sendUpdate } from 'xstate';
55

66
import { AuthEvent, SignUpContext } from '../../types';
77
import { runValidators } from '../../validators';
@@ -132,16 +132,9 @@ export function createSignUpMachine({ services }: SignUpMachineOptions) {
132132
},
133133
},
134134
skipConfirm: {
135-
invoke: {
136-
src: 'signIn',
137-
onDone: {
138-
target: '#signUpActor.resolved',
139-
actions: 'setUser',
140-
},
141-
onError: {
142-
target: 'idle',
143-
actions: 'setRemoteError',
144-
},
135+
always: {
136+
target: '#signUpActor.resolved',
137+
actions: 'setAutoSignInIntent',
145138
},
146139
},
147140

@@ -174,7 +167,7 @@ export function createSignUpMachine({ services }: SignUpMachineOptions) {
174167
onError: [
175168
{
176169
target: '#signUpActor.resolved',
177-
actions: 'setUser',
170+
actions: 'setAutoSignInIntent',
178171
cond: 'isUserAlreadyConfirmed',
179172
},
180173
{ target: 'edit', actions: 'setRemoteError' },
@@ -188,7 +181,7 @@ export function createSignUpMachine({ services }: SignUpMachineOptions) {
188181
src: 'confirmSignUp',
189182
onDone: {
190183
target: '#signUpActor.resolved',
191-
actions: ['setUser'],
184+
actions: 'setAutoSignInIntent',
192185
},
193186
onError: { target: 'edit', actions: 'setRemoteError' },
194187
},
@@ -203,6 +196,7 @@ export function createSignUpMachine({ services }: SignUpMachineOptions) {
203196
return {
204197
user: get(event, 'data.user') || context.user,
205198
authAttributes: { username, password },
199+
intent: context.intent,
206200
};
207201
},
208202
},
@@ -247,28 +241,17 @@ export function createSignUpMachine({ services }: SignUpMachineOptions) {
247241
setCodeDeliveryDetails,
248242
setUser,
249243
sendUpdate: sendUpdate(), // sendUpdate is a HOC
244+
setAutoSignInIntent: assign({ intent: (_) => 'autoSignIn' }),
250245
},
251246
services: {
252-
async signIn(context, event) {
253-
const { user, authAttributes, formValues } = context;
254-
255-
const username =
256-
get(user, 'username') || get(authAttributes, 'username');
257-
const password = get(formValues, 'password');
258-
259-
return await Auth.signIn(username, password);
260-
},
261247
async confirmSignUp(context, event) {
262248
const { user, authAttributes, formValues } = context;
263249
const { confirmation_code: code } = formValues;
264250

265251
const username =
266252
get(user, 'username') || get(authAttributes, 'username');
267-
const { password } = authAttributes;
268-
269-
await services.handleConfirmSignUp({ username, code });
270253

271-
return await Auth.signIn(username, password);
254+
return await services.handleConfirmSignUp({ username, code });
272255
},
273256
async resendConfirmationCode(context, event) {
274257
const { user, authAttributes } = context;

0 commit comments

Comments
 (0)