I am using latest version of Amplify and Vue3 with Vite. Here are the dependencies from package.json
file:
"dependencies": {
"@aws-amplify/ui-vue": "^4.2.16",
"@popperjs/core": "^2.11.8",
"@types/bootstrap": "^5.2.10",
"@types/lodash": "^4.17.7",
"@types/vue-select": "^3.16.8",
"@vuepic/vue-datepicker": "^9.0.3",
"@vueuse/core": "^11.1.0",
"aws-amplify": "^6.6.2",
"axios": "^1.7.7",
"bootstrap": "^5.3.3",
"chart.js": "^4.4.4",
"lodash": "^4.17.21",
"pinia": "^2.2.2",
"v-idle": "^1.0.3",
"vue": "^3.4.37",
"vue-chartjs": "^5.3.1",
"vue-i18n": "^10.0.1",
"vue-router": "^4.4.5",
"vue-select": "^4.0.0-beta.6",
"vue-toastification": "^2.0.0-rc.5",
"vuedraggable": "^4.1.0"
},
The <authenticator>
component from @aws-amplify/ui-vue
is used to display the login form like so:
Whenever I’m calling the logout()
function (by clicking a link) the front-end makes a request to /api/admin/authentication/logout
endpoint to write a log (in the back-end) that the user has logged out:
// TopNav.vue
<template>
<a href="/logout" class="nav-link" role="button" @click.prevent="logout">{{ $t('logout') }}</a>
</template>
<script setup lang="ts">
import { signOut } from 'aws-amplify/auth'
...
// Functions
const logout = async (): Promise<void> => {
await signOut()
}
In the App.vue
file I have this:
<template>
<authenticator :login-mechanisms="['email']" hide-sign-up>
<template v-slot:default>
<div>
<header>
<nav-bar />
<top-nav />
</header>
<div class="content">
<div class="container-fluid">
<router-view />
</div>
</div>
</div>
</template>
</authenticator>
<app-footer :progress="progress" />
</template>
<script setup lang="ts">
import { onBeforeMount, onUnmounted } from 'vue'
import { Authenticator } from '@aws-amplify/ui-vue'
import { fetchAuthSession } from '@aws-amplify/auth'
import { Hub } from 'aws-amplify/utils'
...
import useApp from './composables/use-app'
import useUser from './composables/use-user'
import { useAuthStore } from './stores/auth'
import router from './router'
// Composables
const authStore = useAuthStore()
const { fetchResources, progress } = useApp()
const { homeRoute } = useUser()
// Functions
const onSignedIn = async (): Promise<void> => {
try {
const session = await fetchAuthSession()
if (session.tokens) {
await authStore.signIn(session)
await fetchResources()
await router.push({ name: homeRoute() })
}
} catch(e) {
// Do nothing
}
}
const onSignedOut = async (): Promise<void> => {
await authStore.signOut()
await router.push('/')
}
onBeforeMount(async () => {
Hub.listen('auth', async (data): Promise<void> => {
const { payload } = data
switch(payload.event) {
case 'signedIn':
await onSignedIn()
break
case 'signedOut':
await onSignedOut()
break
}
})
await onSignedIn()
})
onUnmounted(() => {
Hub.listen('auth', () => {})
})
</script>
Here’s the stores/auth.ts
file:
import { defineStore } from 'pinia'
import { AuthSession } from 'aws-amplify/auth'
import http from '../services/http'
import type { IUser } from '../types'
export const useAuthStore = defineStore({
id: 'auth',
state: () => ({
accessToken: '',
idToken: '',
refreshToken: '',
user: {} as IUser
}),
actions: {
setAccessToken(token: string): void {
this.accessToken = token
},
setIdToken(token: string): void {
this.idToken = token
},
setRefreshToken(token: string): void {
this.refreshToken = token
},
setUser(user: IUser): void {
this.user = user
},
async signIn(session: AuthSession, silent: boolean = false): Promise<boolean> {
if (session.tokens?.accessToken) {
this.setAccessToken(session.tokens?.accessToken.toString())
}
if (session.tokens?.idToken) {
this.setIdToken(session.tokens.idToken.toString())
if (! silent) {
await http.post('/admin/authentication/login', {
token: this.getIdToken
})
const response = await http.get('/admin/users/current')
if (response.data.data) {
this.setUser(response.data.data)
}
}
return true
}
return false
},
async signOut(): Promise<void> {
await http.post('/admin/authentication/logout')
this.setAccessToken('')
this.setIdToken('')
this.setUser({} as IUser)
},
},
getters: {
getAccessToken(state): string {
return state.accessToken
},
getIdToken(state): string {
return state.idToken
},
getRefreshToken(state): string {
return state.refreshToken
},
getUser(state): IUser {
return state.user
}
}
})
It turns out that logout()
function mentioned earlier is called multiple times and the API is pinged multiple times resulting in duplicate entry logs. How can I make sure that logout()
function’s API call to /admin/authentication/logout
is triggered only once?