Skip to content

Commit e0fcf36

Browse files
authored
Attempt #2: Add explicit state for initializing authMachine (#1142) (#1162)
* Revert "Revert "Add explicit state for initializing authMachine (#1142)" (#1148)" This reverts commit 6b629e0. * Wait for machine to init before subcomponents render
1 parent 370b84f commit e0fcf36

File tree

9 files changed

+119
-61
lines changed

9 files changed

+119
-61
lines changed

Diff for: .changeset/lucky-numbers-lay.md

+8
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+
"@aws-amplify/ui-vue": patch
5+
"@aws-amplify/ui-angular": patch
6+
---
7+
8+
Add explicit `INIT` step for initializing authMachine

Diff for: docs/src/pages/components/authenticator/LabelsAndTextDemo.tsx

+18-5
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,28 @@ function Screen({ Component }: ScreenProps) {
7171
}
7272

7373
export function LabelsAndTextDemo({ Component }: ScreenProps) {
74+
const OnMachineInit = ({ children }) => {
75+
/**
76+
* This waits for Authenticator machine to init before its inner components
77+
* start consuming machine context.
78+
*/
79+
const { route } = useAuthenticator();
80+
if (!route || route === 'idle' || route === 'setup') return null;
81+
82+
return <>{children}</>;
83+
};
84+
7485
return (
7586
<Authenticator.Provider>
76-
<View data-amplify-authenticator="">
77-
<View data-amplify-container="">
78-
<View data-amplify-body>
79-
<Screen Component={Component} />
87+
<OnMachineInit>
88+
<View data-amplify-authenticator="">
89+
<View data-amplify-container="">
90+
<View data-amplify-body>
91+
<Screen Component={Component} />
92+
</View>
8093
</View>
8194
</View>
82-
</View>
95+
</OnMachineInit>
8396
</Authenticator.Provider>
8497
);
8598
}

Diff for: packages/angular/projects/ui-angular/src/lib/services/authenticator.service.ts

+13-10
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,21 @@ export class AuthenticatorService implements OnDestroy {
3737
signUpAttributes,
3838
socialProviders,
3939
}: AuthenticatorMachineOptions) {
40-
const machine = createAuthenticatorMachine({
41-
initialState,
42-
loginMechanisms,
43-
services,
44-
signUpAttributes,
45-
socialProviders,
40+
const machine = createAuthenticatorMachine();
41+
42+
const authService = interpret(machine).start();
43+
44+
authService.send({
45+
type: 'INIT',
46+
data: {
47+
initialState,
48+
loginMechanisms,
49+
socialProviders,
50+
signUpAttributes,
51+
services,
52+
},
4653
});
4754

48-
const authService = interpret(machine, {
49-
devTools: process.env.NODE_ENV === 'development',
50-
}).start();
51-
5255
this._subscription = authService.subscribe((state) => {
5356
this._authState = state;
5457
this._facade = getServiceContextFacade(state);

Diff for: packages/react/src/components/Authenticator/Provider/index.tsx

+11-10
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,20 @@ const useAuthenticatorValue = ({
2323
signUpAttributes,
2424
services,
2525
}: ProviderProps) => {
26-
const [state, send] = useMachine(
27-
() =>
28-
createAuthenticatorMachine({
26+
const [state, send] = useMachine(() => createAuthenticatorMachine());
27+
28+
React.useEffect(() => {
29+
send({
30+
type: 'INIT',
31+
data: {
2932
initialState,
3033
loginMechanisms,
31-
services,
32-
signUpAttributes,
3334
socialProviders,
34-
}),
35-
{
36-
devTools: process.env.NODE_ENV === 'development',
37-
}
38-
);
35+
signUpAttributes,
36+
services,
37+
},
38+
});
39+
}, []);
3940

4041
const components = React.useMemo(
4142
() => ({ ...defaultComponents, ...customComponents }),

Diff for: packages/react/src/components/Authenticator/Router/index.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export function Router({
6060
{(() => {
6161
switch (route) {
6262
case 'idle':
63+
case 'setup':
6364
return null;
6465
case 'confirmSignUp':
6566
return <ConfirmSignUp />;

Diff for: packages/ui/src/helpers/auth.ts

+2
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ export const getServiceContextFacade = (state: AuthMachineState) => {
226226
switch (true) {
227227
case state.matches('idle'):
228228
return 'idle';
229+
case state.matches('setup'):
230+
return 'setup';
229231
case state.matches('signOut'):
230232
return 'signOut';
231233
case state.matches('authenticated'):

Diff for: packages/ui/src/machines/authenticator/index.ts

+46-25
Original file line numberDiff line numberDiff line change
@@ -9,50 +9,54 @@ import { createSignUpMachine } from './signUp';
99
const DEFAULT_COUNTRY_CODE = '+1';
1010

1111
export type AuthenticatorMachineOptions = AuthContext['config'] & {
12-
initialState?: 'signIn' | 'signUp' | 'resetPassword';
13-
services?: Partial<typeof defaultServices>;
12+
services?: AuthContext['services'];
1413
};
1514

16-
export function createAuthenticatorMachine({
17-
initialState = 'signIn',
18-
loginMechanisms,
19-
signUpAttributes,
20-
socialProviders,
21-
services: customServices,
22-
}: AuthenticatorMachineOptions) {
23-
const services = {
24-
...defaultServices,
25-
...customServices,
26-
};
27-
15+
export function createAuthenticatorMachine() {
2816
return createMachine<AuthContext, AuthEvent>(
2917
{
3018
id: 'authenticator',
3119
initial: 'idle',
3220
context: {
3321
user: undefined,
34-
config: {
35-
loginMechanisms,
36-
signUpAttributes,
37-
socialProviders,
38-
},
22+
config: {},
23+
services: {},
3924
actorRef: undefined,
4025
},
4126
states: {
4227
// See: https://xstate.js.org/docs/guides/communication.html#invoking-promises
4328
idle: {
29+
on: {
30+
INIT: {
31+
target: 'setup',
32+
actions: 'configure',
33+
},
34+
},
35+
},
36+
setup: {
4437
invoke: [
4538
{
4639
// TODO Wait for Auth to be configured
47-
src: 'getCurrentUser',
40+
src: (context, _) => context.services.getCurrentUser(),
4841
onDone: {
4942
actions: 'setUser',
5043
target: 'authenticated',
5144
},
52-
onError: initialState,
45+
onError: [
46+
{
47+
target: 'signUp',
48+
cond: (context) => context.config.initialState === 'signUp',
49+
},
50+
{
51+
target: 'resetPassword',
52+
cond: (context) =>
53+
context.config.initialState === 'resetPassword',
54+
},
55+
{ target: 'signIn' },
56+
],
5357
},
5458
{
55-
src: 'getAmplifyConfig',
59+
src: (context, _) => context.services.getAmplifyConfig(),
5660
onDone: {
5761
actions: 'applyAmplifyConfig',
5862
},
@@ -87,7 +91,7 @@ export function createAuthenticatorMachine({
8791
on: {
8892
SIGN_IN: 'signIn',
8993
'done.invoke.signUpActor': {
90-
target: 'idle',
94+
target: 'setup',
9195
actions: 'setUser',
9296
},
9397
},
@@ -159,7 +163,14 @@ export function createAuthenticatorMachine({
159163
cliLoginMechanisms.push('username');
160164
}
161165

162-
// Prefer explicitly configured settings over default CLI values
166+
// Prefer explicitly configured settings over default CLI values\
167+
168+
const {
169+
loginMechanisms,
170+
signUpAttributes,
171+
socialProviders,
172+
initialState,
173+
} = context.config;
163174
return {
164175
loginMechanisms: loginMechanisms ?? cliLoginMechanisms,
165176
signUpAttributes:
@@ -171,11 +182,13 @@ export function createAuthenticatorMachine({
171182
])
172183
),
173184
socialProviders: socialProviders ?? cliSocialProviders.sort(),
185+
initialState,
174186
};
175187
},
176188
}),
177189
spawnSignInActor: assign({
178190
actorRef: (context, event) => {
191+
const { services } = context;
179192
const actor = signInActor({ services }).withContext({
180193
authAttributes: event.data?.authAttributes,
181194
user: event.data?.user,
@@ -192,6 +205,7 @@ export function createAuthenticatorMachine({
192205
}),
193206
spawnSignUpActor: assign({
194207
actorRef: (context, event) => {
208+
const { services } = context;
195209
const actor = createSignUpMachine({ services }).withContext({
196210
authAttributes: event.data?.authAttributes ?? {},
197211
country_code: DEFAULT_COUNTRY_CODE,
@@ -207,6 +221,7 @@ export function createAuthenticatorMachine({
207221
}),
208222
spawnResetPasswordActor: assign({
209223
actorRef: (context, event) => {
224+
const { services } = context;
210225
const actor = resetPasswordActor({ services }).withContext({
211226
formValues: {},
212227
touched: {},
@@ -225,6 +240,13 @@ export function createAuthenticatorMachine({
225240
return spawn(actor, { name: 'signOutActor' });
226241
},
227242
}),
243+
configure: assign((_, event) => {
244+
const { services: customServices, ...config } = event.data;
245+
return {
246+
services: { ...defaultServices, ...customServices },
247+
config,
248+
};
249+
}),
228250
},
229251
guards: {
230252
shouldRedirectToSignUp: (_, event): boolean => {
@@ -236,7 +258,6 @@ export function createAuthenticatorMachine({
236258
return event.data.intent === 'confirmPasswordReset';
237259
},
238260
},
239-
services,
240261
}
241262
);
242263
}

Diff for: packages/ui/src/types/authMachine.ts

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { CognitoUser, CodeDeliveryDetails } from 'amazon-cognito-identity-js';
22
import { Interpreter, State } from 'xstate';
33
import { ValidationError } from './validator';
4+
import { defaultServices } from '../machines/authenticator/defaultServices';
45

56
export type AuthFormData = Record<string, string>;
67

@@ -10,7 +11,9 @@ export interface AuthContext {
1011
loginMechanisms?: LoginMechanism[];
1112
signUpAttributes?: SignUpAttribute[];
1213
socialProviders?: SocialProvider[];
14+
initialState?: 'signIn' | 'signUp' | 'resetPassword';
1315
};
16+
services?: Partial<typeof defaultServices>;
1417
user?: CognitoUserAmplify;
1518
username?: string;
1619
password?: string;
@@ -131,6 +134,7 @@ export type AuthEventTypes =
131134
| 'SIGN_UP'
132135
| 'SKIP'
133136
| 'SUBMIT'
137+
| 'INIT'
134138
| InvokeActorEventTypes;
135139

136140
export enum AuthChallengeNames {

Diff for: packages/vue/src/components/authenticator.vue

+16-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script setup lang="ts">
22
import { useAuth } from '../composables/useAuth';
3-
import { ref, computed, useAttrs, watch, Ref } from 'vue';
3+
import { ref, computed, useAttrs, watch, Ref, onMounted } from 'vue';
44
import { useActor, useInterpret } from '@xstate/vue';
55
import {
66
getActorState,
@@ -58,21 +58,26 @@ const emit = defineEmits([
5858
'verifyUserSubmit',
5959
'confirmVerifyUserSubmit',
6060
]);
61-
const machine = createAuthenticatorMachine({
62-
initialState,
63-
loginMechanisms,
64-
services,
65-
signUpAttributes,
66-
socialProviders,
67-
});
61+
const machine = createAuthenticatorMachine();
6862
69-
const service = useInterpret(machine, {
70-
devTools: process.env.NODE_ENV === 'development',
71-
});
63+
const service = useInterpret(machine);
7264
7365
const { state, send } = useActor(service);
7466
useAuth(service);
7567
68+
onMounted(() => {
69+
send({
70+
type: 'INIT',
71+
data: {
72+
initialState,
73+
loginMechanisms,
74+
socialProviders,
75+
signUpAttributes,
76+
services,
77+
},
78+
});
79+
});
80+
7681
const actorState = computed(() => getActorState(state.value));
7782
7883
const signInComponent = ref();

0 commit comments

Comments
 (0)