Skip to content

Commit

Permalink
Listen to Auth Hub events (#1153)
Browse files Browse the repository at this point in the history
* send SIGN_OUT signal on sign out event

* [REVERT] Publish to @experimental

* [Revert] Fix typo

* [REVERT] try removing branch restriction

* Give provider environment

* Remove unused var

* Use shared service

* Add react example + .feature

* Remove unrelated changes

* Add angular example

* Add vue example

* Fix route in .feature

* Remove publishing action

* Remove unused var

* Create fast-fireants-help.md
  • Loading branch information
wlee221 authored Jan 27, 2022
1 parent b358680 commit 3afdc1f
Show file tree
Hide file tree
Showing 16 changed files with 151 additions and 9 deletions.
8 changes: 8 additions & 0 deletions .changeset/fast-fireants-help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@aws-amplify/ui-react": patch
"@aws-amplify/ui": patch
"@aws-amplify/ui-vue": patch
"@aws-amplify/ui-angular": patch
---

Listen to Auth Hub events
11 changes: 8 additions & 3 deletions examples/angular/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CustomSignUpFieldsComponent } from 'src/pages/ui/components/authenticator/custom-sign-up-fields/custom-sign-up-fields.component';
import { CustomSlotsComponent } from 'src/pages/ui/components/authenticator/custom-slots/custom-slots.component';
import { HubEventsComponent } from 'src/pages/ui/components/authenticator/hub-events/hub-events.component';
import { I18nComponent } from 'src/pages/ui/components/authenticator/i18n/i18n.component';
import { ModalComponent } from 'src/pages/ui/components/authenticator/modal/modal.component';
import { ResetPasswordComponent } from 'src/pages/ui/components/authenticator/reset-password/reset-password.component';
Expand All @@ -10,13 +13,11 @@ import { SignInTOTPSMSComponent } from 'src/pages/ui/components/authenticator/si
import { SignInWithEmailComponent } from 'src/pages/ui/components/authenticator/sign-in-with-email/sign-in-with-email.component';
import { SignInWithPhoneComponent } from 'src/pages/ui/components/authenticator/sign-in-with-phone/sign-in-with-phone.component';
import { SignInWithUsernameComponent } from 'src/pages/ui/components/authenticator/sign-in-with-username/sign-in-with-username.component';
import { SignUpWithEmailComponent } from 'src/pages/ui/components/authenticator/sign-up-with-email/sign-up-with-email.component';
import { SignUpWithAttributesComponent } from 'src/pages/ui/components/authenticator/sign-up-with-attributes/sign-up-with-attributes.component';
import { SignUpWithEmailComponent } from 'src/pages/ui/components/authenticator/sign-up-with-email/sign-up-with-email.component';
import { SignUpWithEmailLambdaComponent } from 'src/pages/ui/components/authenticator/sign-up-with-email-lambda/sign-up-with-email-lambda.component';
import { SignUpWithPhoneComponent } from 'src/pages/ui/components/authenticator/sign-up-with-phone/sign-up-with-phone.component';
import { SignUpWithUsernameComponent } from 'src/pages/ui/components/authenticator/sign-up-with-username/sign-up-with-username.component';
import { CustomSignUpFieldsComponent } from 'src/pages/ui/components/authenticator/custom-sign-up-fields/custom-sign-up-fields.component';
import { CustomSlotsComponent } from 'src/pages/ui/components/authenticator/custom-slots/custom-slots.component';

const routes: Routes = [
{
Expand All @@ -27,6 +28,10 @@ const routes: Routes = [
path: 'ui/components/authenticator/custom-slots',
component: CustomSlotsComponent,
},
{
path: 'ui/components/authenticator/hub-events',
component: HubEventsComponent,
},
{
path: 'ui/components/authenticator/modal',
component: ModalComponent,
Expand Down
2 changes: 2 additions & 0 deletions examples/angular/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { AppComponent } from './app.component';

import { CustomSignUpFieldsComponent } from 'src/pages/ui/components/authenticator/custom-sign-up-fields/custom-sign-up-fields.component';
import { CustomSlotsComponent } from 'src/pages/ui/components/authenticator/custom-slots/custom-slots.component';
import { HubEventsComponent } from 'src/pages/ui/components/authenticator/hub-events/hub-events.component';
import { I18nComponent } from 'src/pages/ui/components/authenticator/i18n/i18n.component';
import { ModalComponent } from 'src/pages/ui/components/authenticator/modal/modal.component';
import { ResetPasswordComponent } from 'src/pages/ui/components/authenticator/reset-password/reset-password.component';
Expand All @@ -32,6 +33,7 @@ import { SignUpWithUsernameComponent } from 'src/pages/ui/components/authenticat
AppComponent,
CustomSignUpFieldsComponent,
CustomSlotsComponent,
HubEventsComponent,
I18nComponent,
ModalComponent,
ResetPasswordComponent,
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import awsExports from '@environments/auth-with-email/src/aws-exports';
export default awsExports;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<amplify-authenticator>
<ng-template amplifySlot="authenticated" let-user="user">
<h2>Welcome, {{ user.username }}!</h2>
<button (click)="signOut()">Sign Out</button>
</ng-template>
</amplify-authenticator>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Component } from '@angular/core';
import Amplify, { Auth } from 'aws-amplify';
import awsExports from './aws-exports';

@Component({
selector: 'hub-events',
templateUrl: 'hub-events.component.html',
})
export class HubEventsComponent {
constructor() {
Amplify.configure(awsExports);
}

public signOut(): void {
Auth.signOut();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import awsExports from '@environments/auth-with-email/src/aws-exports';
export default awsExports;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Amplify, Auth } from 'aws-amplify';

import { withAuthenticator } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';

import awsExports from './aws-exports';
Amplify.configure(awsExports);

function App({}) {
return <button onClick={() => Auth.signOut()}>Sign out</button>;
}

export default withAuthenticator(App);
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import awsExports from '@environments/auth-with-email/src/aws-exports';
export default awsExports;
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script setup lang="ts">
import Amplify, { Auth } from 'aws-amplify';
import { Authenticator } from '@aws-amplify/ui-vue';
import '@aws-amplify/ui-vue/styles.css';
import aws_exports from './aws-exports';
Amplify.configure(aws_exports);
</script>

<template>
<authenticator>
<template v-slot="{ user }">
<h1>Hello {{ user.username }}!</h1>
<button @click="Auth.signOut()">Sign Out</button>
</template>
</authenticator>
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
createAuthenticatorMachine,
getSendEventAliases,
getServiceContextFacade,
getServiceFacade,
listenToAuthHub,
} from '@aws-amplify/ui';
import { Event, interpret, Subscription } from 'xstate';
import { AuthSubscriptionCallback } from '../common';
Expand All @@ -27,7 +27,8 @@ export class AuthenticatorService implements OnDestroy {
private _authState: AuthMachineState;
private _authService: AuthInterpreter;
private _sendEventAliases: ReturnType<typeof getSendEventAliases>;
private _subscription: Subscription;
private _machineSubscription: Subscription;
private _hubSubscription: ReturnType<typeof listenToAuthHub>;
private _facade: ReturnType<typeof getServiceContextFacade>;

public startMachine({
Expand All @@ -52,17 +53,19 @@ export class AuthenticatorService implements OnDestroy {
},
});

this._subscription = authService.subscribe((state) => {
this._machineSubscription = authService.subscribe((state) => {
this._authState = state;
this._facade = getServiceContextFacade(state);
});

this._hubSubscription = listenToAuthHub(authService.send);
this._sendEventAliases = getSendEventAliases(authService.send);
this._authService = authService;
}

ngOnDestroy(): void {
if (this._subscription) this._subscription.unsubscribe();
if (this._machineSubscription) this._machineSubscription.unsubscribe();
if (this._hubSubscription) this._hubSubscription();
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Feature: Hub Events
Authenticator listens to external Auth Hub events from 'aws-amplify'. For
example, Authenticator will automatically sign out the user if it gets a
"signOut" hub event.

Background:
Given I'm running the example "/ui/components/authenticator/hub-events"

@angular @react @vue
Scenario: Sign in with confirmed credentials then sign out
When I type my "email" with status "CONFIRMED"
And I type my password
And I click the "Sign in" button
Then I see "Sign out"
And I click the "Sign out" button
Then I see "Sign in"

Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
getSendEventAliases,
getServiceContextFacade,
AuthMachineSend,
listenToAuthHub,
} from '@aws-amplify/ui';
import { useSelector, useInterpret } from '@xstate/react';
import isEmpty from 'lodash/isEmpty';
Expand Down Expand Up @@ -46,6 +47,14 @@ export const Provider = ({ children }) => {
? currentProviderVal
: parentProviderVal;

const {
service: { send },
} = value;

React.useEffect(() => {
return listenToAuthHub(send);
}, []);

return (
<AuthenticatorContext.Provider value={value}>
{children}
Expand Down
24 changes: 23 additions & 1 deletion packages/ui/src/helpers/auth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import includes from 'lodash/includes';
import { Hub } from 'aws-amplify';
import { Sender } from 'xstate';
import includes from 'lodash/includes';

import { AuthContext } from '..';
import {
Expand All @@ -10,6 +11,7 @@ import {
AuthEventData,
AuthEventTypes,
AuthInputAttributes,
AuthMachineSend,
AuthMachineState,
LoginMechanism,
LoginMechanismArray,
Expand Down Expand Up @@ -281,3 +283,23 @@ export const getServiceFacade = ({ send, state }) => {
...serviceContext,
};
};

/**
* Listens to external auth Hub events and sends corresponding event to
* the `authService` of interest
*
* @param send - `send` function associated with the `authService` of interest
*
* @returns function that unsubscribes to the hub evenmt
*/
export const listenToAuthHub = (send: AuthMachineSend) => {
return Hub.listen('auth', (data) => {
switch (data.payload.event) {
// TODO: We can add more cases here, according to
// https://docs.amplify.aws/lib/auth/auth-events/q/platform/js/
case 'signOut':
send('SIGN_OUT');
break;
}
});
};
17 changes: 16 additions & 1 deletion packages/vue/src/components/authenticator.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
<script setup lang="ts">
import { useAuth } from '../composables/useAuth';
import { ref, computed, useAttrs, watch, Ref, onMounted } from 'vue';
import {
ref,
computed,
useAttrs,
watch,
Ref,
onMounted,
onUnmounted,
} from 'vue';
import { useActor, useInterpret } from '@xstate/vue';
import {
getActorState,
Expand All @@ -10,6 +18,7 @@ import {
translate,
CognitoUserAmplify,
SocialProvider,
listenToAuthHub,
} from '@aws-amplify/ui';
import SignIn from './sign-in.vue';
Expand Down Expand Up @@ -61,11 +70,13 @@ const emit = defineEmits([
const machine = createAuthenticatorMachine();
const service = useInterpret(machine);
let unsubscribeHub: ReturnType<typeof listenToAuthHub>;
const { state, send } = useActor(service);
useAuth(service);
onMounted(() => {
unsubscribeHub = listenToAuthHub(send);
send({
type: 'INIT',
data: {
Expand All @@ -78,6 +89,10 @@ onMounted(() => {
});
});
onUnmounted(() => {
if (unsubscribeHub) unsubscribeHub();
});
const actorState = computed(() => getActorState(state.value));
const signInComponent = ref();
Expand Down

0 comments on commit 3afdc1f

Please sign in to comment.