Skip to content

Commit

Permalink
refactor(ui-core): auth machine refactor with actors (aws-amplify#180)
Browse files Browse the repository at this point in the history
* setup local dev helper

* Set tab size

* initial directory setup

* sign in actor skeleton implementation

* sign in staging

* Temp workaround for sign in to render

* signIn initial refactor

* signUp initial refactor

* removed unused import

* Remove unused actors

* Remove unused exports

* spawn signIn and signUp with context

* Send CHANGE event instead of INPUT

* signIn with context values

* fix typo

* Rename INPUT to CHANGE

* Set user on success

* Enable uncofirmed signIn -> confirmSignUp use case

* get context from actor whenever needed

* Be explicit about what each actor passes

* implement signOut actor

* use actor context

* formalize logic for passing contexts between actors

* remove TODO

* fix reference

* fix function signature

* Remove unneeded context

* Removed unused export

* Use intent instead of an explicit error

* enable auto sign in

* send autoSignIn intent from signUp

* Move context definitions to /types

* Update actor changes to forceNewPassword

* get challengeName from actor

* provide initial context and don't use sync

* Reflect actor changes to React!

* sendUpdate on each validation

* vue refactors!

* Don't finish on federatedSignIn

* fix typo

* implement resetPassword

* use formValues from context

* pass login_mechanism

* display error on signIn

* persist through authAttributes

* Remove unused var

* Remove unused actions

* Remove unnecessary check

* clear formValues on transition

* Remove context from actor def

* Remove unused transition

* Update packages/vue/src/components/confirm-sign-up.vue

Co-authored-by: Erik Hanchett <[email protected]>

* Use first available username

* Pass username from signIn with `authAttributes`

* Use username from authAttributes.username

* Remove unused export

* Separaete out invoke event types

* Use Record<PropertyKey, any>

* call federatedSignIn from confirm-sign-up

* Rename state to _state

* Remove unnecessary state export

* check both username sources

* Strictly type helper functions

* Strongly type helpers in Vue

* React: use tryped result from helpers

* Add missed assertions

* More vue strong typings

* Fianl strongly typed actorState!

* Update packages/core/src/types/authMachine.ts

Thanks @eddiekeller!

Co-authored-by: Eddie Keller <[email protected]>

* Update packages/react/src/components/Authenticator/SignIn/SignIn.tsx

* Forward SIGN_IN event

* Remove unused imports

* Fix angular build

* Type ErrorText context

* Provide default context

* get user from context

* Remove autoSignIn state and go directly to signIn.submit

* Remove unused import/var

* Update packages/core/src/machines/authMachine.ts

Co-authored-by: Erik Hanchett <[email protected]>
Co-authored-by: Eddie Keller <[email protected]>
  • Loading branch information
3 people authored Aug 17, 2021
1 parent 30bcaf1 commit 31a11b3
Show file tree
Hide file tree
Showing 46 changed files with 1,242 additions and 926 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.insertSpaces": true,
"editor.tabSize": 2,
"files.insertFinalNewline": true,
"prettier.requireConfig": true,
"typescript.tsdk": "node_modules/typescript/lib",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,56 +33,54 @@
<ng-container
[ngTemplateOutlet]="customComponents.signIn || signIn"
[ngTemplateOutletContext]="context()"
*ngIf="
getAuthState().matches('signIn') && !getAuthState().matches('authenticated')
"
*ngIf="actorState?.matches('signIn')"
>
</ng-container>

<!-- signUp component -->
<ng-container
[ngTemplateOutlet]="customComponents.signUp || signUp"
[ngTemplateOutletContext]="context()"
*ngIf="getAuthState().matches('signUp')"
*ngIf="actorState?.matches('signUp')"
>
</ng-container>

<!-- confirmSignUp content -->
<ng-container
[ngTemplateOutlet]="customComponents.confirmSignUp || confirmSignUp"
[ngTemplateOutletContext]="context()"
*ngIf="getAuthState().matches('confirmSignUp')"
*ngIf="actorState?.matches('confirmSignUp')"
>
</ng-container>

<!-- confirmSignIn content -->
<ng-container
[ngTemplateOutlet]="customComponents.confirmSignIn || confirmSignIn"
[ngTemplateOutletContext]="context()"
*ngIf="getAuthState().matches('confirmSignIn')"
*ngIf="actorState?.matches('confirmSignIn')"
>
</ng-container>

<!-- setupTotp content -->
<ng-container
[ngTemplateOutlet]="customComponents.setupTOTP || setupTOTP"
[ngTemplateOutletContext]="context()"
*ngIf="getAuthState().matches('setupTOTP')"
*ngIf="actorState?.matches('setupTOTP')"
>
</ng-container>

<!-- forceNewPassword content -->
<ng-container
[ngTemplateOutlet]="customComponents.forceNewPassword || forceNewPassword"
[ngTemplateOutletContext]="context()"
*ngIf="getAuthState().matches('forceNewPassword')"
*ngIf="actorState.matches('forceNewPassword')"
>
</ng-container>

<!-- signedIn content -->
<ng-container
[ngTemplateOutlet]="customComponents.authenticated || authenticated"
[ngTemplateOutletContext]="context()"
*ngIf="getAuthState().matches('authenticated')"
*ngIf="authenticatorState.matches('authenticated')"
>
</ng-container>
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { AuthState } from '../../common/types';
import { AmplifyOverrideDirective } from '../../directives/amplify-override.directive';
import { StateMachineService, AuthPropService } from '../../services';
import { CustomComponents } from '../../common';
import { State } from 'xstate';
import { getActorState } from '@aws-amplify/ui-core';

@Component({
selector: 'amplify-authenticator',
Expand Down Expand Up @@ -53,8 +53,11 @@ export class AmplifyAuthenticatorComponent implements AfterContentInit {
/**
* Class Functions
*/
public get actorState() {
return getActorState(this.stateMachine.authState);
}

public getAuthState(): State<any> {
public get authenticatorState() {
return this.stateMachine.authState;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@ import {
import { Logger } from '@aws-amplify/core';
import { AuthPropService, StateMachineService } from '../../services';
import { Subscription } from 'xstate';
import { AuthChallengeNames, AuthMachineState } from '@aws-amplify/ui-core';
import {
AuthChallengeNames,
AuthMachineState,
getActorContext,
getActorState,
SignInContext,
SignInState,
} from '@aws-amplify/ui-core';

const logger = new Logger('ConfirmSignIn');

Expand Down Expand Up @@ -51,7 +58,9 @@ export class AmplifyConfirmSignInComponent
}

setHeaderText(): void {
const { challengeName } = this.stateMachine.context;
const state = this.stateMachine.authState;
const actorContext: SignInContext = getActorContext(state);
const { challengeName } = actorContext;
switch (challengeName) {
case AuthChallengeNames.SOFTWARE_TOKEN_MFA:
// TODO: this string should be centralized and translated from ui-core.
Expand All @@ -66,15 +75,16 @@ export class AmplifyConfirmSignInComponent
}

onStateUpdate(state: AuthMachineState): void {
this.remoteError = state.context.remoteError;
this.isPending = !state.matches('confirmSignIn.edit');
const actorState: SignInState = getActorState(state);
this.remoteError = actorState.context.remoteError;
this.isPending = !actorState.matches('confirmSignIn.edit');
}

onInput(event: Event) {
event.preventDefault();
const { name, value } = <HTMLInputElement>event.target;
this.stateMachine.send({
type: 'INPUT',
type: 'CHANGE',
data: { name, value },
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<form data-amplify-form (submit)="onSubmit($event)" (input)="onInput($event)">
<fieldset data-amplify-fieldset [disabled]="isPending">
<amplify-user-name-alias
[initialValue]="username"
[initialValue]="username ? username : null"
[disabled]="!!username"
></amplify-user-name-alias>
<amplify-input
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { Component, HostBinding, TemplateRef } from '@angular/core';
import { AuthMachineState } from '@aws-amplify/ui-core';
import {
AuthMachineState,
getActorContext,
getActorState,
SignUpContext,
SignUpState,
} from '@aws-amplify/ui-core';
import { Logger } from '@aws-amplify/core';
import { Subscription } from 'xstate';
import { AuthPropService, StateMachineService } from '../../services';
Expand Down Expand Up @@ -33,16 +39,14 @@ export class AmplifyConfirmSignUpComponent {
}

setUsername() {
const { user, formValues } = this.stateMachine.context;
/**
* TODO (cross-framework): look for ways to persist username without
* persisting formValues across auth states.
*/
const username = user?.username ?? formValues.username;
const state = this.stateMachine.authState;
const actorContext: SignUpContext = getActorContext(state);
const { user, authAttributes } = actorContext;
const username = user?.username ?? authAttributes?.username;
if (username) {
this.username = username;
this.stateMachine.send({
type: 'INPUT',
type: 'CHANGE',
data: { name: 'username', value: this.username },
});
}
Expand All @@ -60,8 +64,9 @@ export class AmplifyConfirmSignUpComponent {
}

onStateUpdate(state: AuthMachineState): void {
this.remoteError = state.context.remoteError;
this.isPending = !state.matches('confirmSignUp.edit');
const actorState: SignUpState = getActorState(state);
this.remoteError = actorState.context.remoteError;
this.isPending = !actorState.matches('confirmSignUp.edit');
}

toSignIn(): void {
Expand All @@ -81,15 +86,16 @@ export class AmplifyConfirmSignUpComponent {
$event.preventDefault();
const { name, value } = $event.target;
this.stateMachine.send({
type: 'INPUT',
type: 'CHANGE',
data: { name, value },
});
}

async onSubmit(event: Event): Promise<void> {
onSubmit(event: Event) {
event.preventDefault();
const formValues = this.stateMachine.context.formValues;
// get form data
const state = this.stateMachine.authState;
const actorContext: SignUpContext = getActorContext(state);
const { formValues } = actorContext;
const { username, confirmation_code } = formValues;

this.stateMachine.send({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ import {
} from '@angular/core';
import { Subscription } from 'xstate';
import { Logger } from '@aws-amplify/core';
import { AuthMachineState } from '@aws-amplify/ui-core';
import {
AuthMachineState,
getActorContext,
getActorState,
SignInContext,
SignInState,
} from '@aws-amplify/ui-core';
import { AuthPropService, StateMachineService } from '../../services';

const logger = new Logger('ForceNewPassword');
Expand Down Expand Up @@ -53,8 +59,9 @@ export class AmplifyForceNewPasswordComponent
}

onStateUpdate(state: AuthMachineState): void {
this.remoteError = state.context.remoteError;
this.isPending = !state.matches('forceNewPassword.edit');
const actorState: SignInState = getActorState(state);
this.remoteError = actorState.context.remoteError;
this.isPending = !actorState.matches('forceNewPassword.edit');
}

toSignIn(): void {
Expand All @@ -65,14 +72,17 @@ export class AmplifyForceNewPasswordComponent
event.preventDefault();
const { name, value } = <HTMLInputElement>event.target;
this.stateMachine.send({
type: 'INPUT',
type: 'CHANGE',
data: { name, value },
});
}

onSubmit(event: Event) {
event.preventDefault();
const formValues = this.stateMachine.context.formValues;
// consider stateMachine directly providing actorState / actorContext
const state = this.stateMachine.authState;
const actorState: SignInContext = getActorContext(state);
const { formValues } = actorState;

this.stateMachine.send({
type: 'SUBMIT',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ import {
} from '@angular/core';
import { Subscription } from 'xstate';
import QRCode from 'qrcode';
import { Logger } from '@aws-amplify/core';
import { AuthMachineState } from '@aws-amplify/ui-core';
import Auth from '@aws-amplify/auth';
import { Auth, Logger } from 'aws-amplify';
import {
AuthMachineState,
getActorState,
SignInState,
} from '@aws-amplify/ui-core';
import { AuthPropService, StateMachineService } from '../../services';

const logger = new Logger('SetupTotp');
Expand Down Expand Up @@ -53,8 +56,9 @@ export class AmplifySetupTotpComponent
}

onStateUpdate(state: AuthMachineState): void {
this.remoteError = state.context.remoteError;
this.isPending = !state.matches('setupTOTP.edit');
const actorState: SignInState = getActorState(state);
this.remoteError = actorState.context.remoteError;
this.isPending = !actorState.matches('setupTOTP.edit');
}

async generateQRCode() {
Expand All @@ -77,7 +81,7 @@ export class AmplifySetupTotpComponent
event.preventDefault();
const { name, value } = <HTMLInputElement>event.target;
this.stateMachine.send({
type: 'INPUT',
type: 'CHANGE',
data: { name, value },
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import {
} from '@angular/core';
import { AuthPropService, StateMachineService } from '../../services';
import { Subscription } from 'xstate';
import { AuthMachineState } from '@aws-amplify/ui-core';
import {
AuthMachineState,
getActorState,
SignInState,
} from '@aws-amplify/ui-core';

const logger = new Logger('SignIn');

Expand Down Expand Up @@ -54,8 +58,9 @@ export class AmplifySignInComponent
}

onStateUpdate(state: AuthMachineState): void {
this.remoteError = state.context.remoteError;
this.isPending = !state.matches('signIn.edit');
const actorState: SignInState = getActorState(state);
this.remoteError = actorState.context.remoteError;
this.isPending = !actorState.matches('signIn.edit');
}

toSignUp(): void {
Expand All @@ -66,18 +71,16 @@ export class AmplifySignInComponent
event.preventDefault();
const { name, value } = <HTMLInputElement>event.target;
this.stateMachine.send({
type: 'INPUT',
type: 'CHANGE',
data: { name, value },
});
}

async onSubmit(event: Event): Promise<void> {
event.preventDefault();
const formValues = this.stateMachine.context.formValues;

this.stateMachine.send({
type: 'SUBMIT',
data: formValues,
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ import {
} from '@angular/core';
import { AuthPropService, StateMachineService } from '../../services';
import { Subscription } from 'xstate';
import { AuthMachineState, getConfiguredAliases } from '@aws-amplify/ui-core';
import {
AuthMachineState,
getActorState,
getConfiguredAliases,
SignUpState,
} from '@aws-amplify/ui-core';

const logger = new Logger('SignUp');
@Component({
Expand Down Expand Up @@ -57,10 +62,11 @@ export class AmplifySignUpComponent
}

private onStateUpdate(state: AuthMachineState): void {
this.remoteError = state.context.remoteError;
this.isPending = state.matches({
const actorState: SignUpState = getActorState(state);
this.remoteError = actorState.context.remoteError;
this.isPending = !actorState.matches({
signUp: {
submission: 'valid',
submission: 'idle',
},
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { Component, Input } from '@angular/core';
import { AuthInputAttributes } from '@aws-amplify/ui-core';
import {
ActorContextWithForms,
AuthInputAttributes,
getActorContext,
} from '@aws-amplify/ui-core';
import { getAttributeMap } from '../../common';
import { StateMachineService } from '../../services';

Expand Down Expand Up @@ -29,7 +33,10 @@ export class AmplifyInputComponent {
}

get error(): string {
const { validationError } = this.stateMachine.context;
const formContext: ActorContextWithForms = getActorContext(
this.stateMachine.authState
);
const { validationError } = formContext;
return validationError[this.name];
}

Expand Down
Loading

0 comments on commit 31a11b3

Please sign in to comment.