amplify-ui
amplify-ui copied to clipboard
Vue: why is the <authenticator> component now required for useAuthenticator?
I'm attempting to upgrade from @aws-amplify/ui-vue
3.1.29 to 4.2.1.
Previously, my Vue application used useAuthenticator
in various places to control the UI based on authentication status.
If a user wanted to sign in, I redirected to an authentication route which contained the authenticator
component.
Upon upgrading, it appears this strategy doesn't work.
I see in the docs (not sure if this was there before):
You must render the
UI component before using the useAuthenticator composable. useAuthenticator was designed to retrieve UI specific state such as route and user and should not be used without the UI component.
What am I supposed to do if I don't want to wrap my entire application in the authenticator
component? From what I understand, this will automatically redirect any signed out users to the sign in view.
This is a personal show stopper for me.
Thankfully moving logic from components to composables in vue is usually pretty easy and it should be a quick fix.
@robokozo @jtleniger Can you provide more details / code samples of what specifically isn't working for you? I don't think this behavior should have changed between 3 and 4. We have a e2e test that tests this scenario of using useAuthenticator
without rendering the authenticator
, so I'm trying to understand if there's a gap in our testing: https://github.com/aws-amplify/amplify-ui/blob/main/examples/vue/src/pages/ui/components/authenticator/auth-status/index.vue
Would also note that the docs seem to be out of date here, we'll get those updated.
I'm not at my personal computer atm, but the basic idea was something like.. imagine 2 routes
- App.vue
<script setup>
</script>
<template>
<Link to="/sign-in">
<Link to="/other">
<router-view></router-view>
</template>
- SignIn.vue
<script setup>
const auth = useAuthenticator()
</script>
<template>
<authenticator></authenticator>
<h1>Hello {{ auth.user?.username }}!</h1>
</template>
- Other.vue
<script setup>
const auth = useAuthenticator()
</script>
<template>
<h1>Hello {{ auth.user?.username }}!</h1>
</template>
When navigating from sign-in
to other
, Other.vue maintains the user information and displays the username
If I refresh from the other
route, i do not get the auth information.
I took a look at the <authenticator>
component in the repo, and it seems to initialize the state machine for useAuth. I imagine this is related?
https://github.com/aws-amplify/amplify-ui/blob/77551bc86b01e3150232b158f0aa683bef0cf12b/packages/vue/src/components/authenticator.vue#L71-L92
I've created a minimum example reproducing the behavior we're talking about: https://github.com/jtleniger/amplify-ui-5028
We have a e2e test that tests this scenario of using useAuthenticator without rendering the authenticator
Interesting! The docs seem to agree with the behavior, stating this shouldn't work.
Something to add, I'm not necessarily suggesting the behavior should be reverted to what it was in version 3.
I'm just wondering with this change, what is the intended way to achieve what the example repo is trying to do?
I ran back to test my code on my personal computer and I can't recreate my particular problem anymore. (Sorry if I raised any alarms.. I must have been really tired last night ^_^)
@jtleniger can you try moving the useAuthenticator inside the route guard?
beforeEnter: () => {
const auth = useAuthenticator();
if (auth.authStatus !== 'authenticated') {
return { name: 'signin'}
}
},
I can try that but the issue isn't related to the route guard.
The issue is that while signed in, on a route which does not render the authenticator
component (home or protected in the example repo), if the page is refreshed, useAuthenticator
does not have the correct state.
Hi @jtleniger,
Moving the useAuthenticator into the route guard does seem fix the issue with authStatus
not updating correctly inside the components, but it still has an issue in your route guard because the initial authStatus
is "configuring", and thus you will still redirect. After moving useAuthenticator into the auth guard, the authStatus is will be "configuring" and then updated next to "authenticated" if rendering a component, but the redirect has already happened.
However, in the case of your router logic you may find it simpler to just check directly whether the user is authenticated via the JS getCurrentUser
API:
import { getCurrentUser } from 'aws-amplify/auth'
const router = createRouter({
{
path: '/protected',
name: 'protected',
beforeEnter: async () => {
try {
await getCurrentUser();
} catch (e) {
return { name: 'signin'}
}
},
component: ProtectedView
},
//...
Showing authenticated state after page refresh:
Let me know if this works for you!
Oh interesting.
So it's possible to break the the auth status by calling useAuthenticator
at the top level of the router file.
I'm inferring here, but is it due to the fact that app.use(router)
, thus useAuthenticator
is called before Amplify.configure
?
Not sure if this should be considered a bug; perhaps a note in the docs or maybe useAuthenticator
should throw if it is called before configuration? Assuming that's the issue.
Feel free to close this, thanks for your help!
I think the main issue here is that useAuthenticator
is a composable that's mean to be used in a component, but not intended to be used in router code. Its initial value is always going to be 'configuring', so it's better to rely on the lower level getCurrentUser
JS API here. Going to close this out, since it seems like your question was answered, but please feel free to open a new issue if you're still having issues!