Vue client
WIPVue 3 composables - authUseSession, authUseSignIn, authUseSignOut. Reactive across components and across browser tabs.
Setup
src/main.ts
import { createApp } from 'vue'
import { createAuth } from '@gentleduck/auth/client/vue'
import App from './App.vue'
const auth = createAuth({
baseUrl: '/auth',
})
createApp(App).use(auth).mount('#app')
authUseSession
<script setup lang="ts">
import { authUseSession } from '@gentleduck/auth/client/vue'
const { data, status } = authUseSession()
</script>
<template>
<div v-if="status === 'loading'">Loading...</div>
<div v-else-if="status === 'guest'">
<SignInForm />
</div>
<div v-else>
Hi, {{ data.identity.profile.displayName }}
<SignOutButton />
</div>
</template>
authUseSession() returns a Ref (under the hood, it's a composable
backed by ref() + the vanilla client's onChange subscription).
Use it in <template> directly.
authUseSignIn / authUseSignOut
<script setup lang="ts">
import { authUseSignIn, authUseSignOut } from '@gentleduck/auth/client/vue'
const signIn = authUseSignIn()
const signOut = authUseSignOut()
const email = ref('')
const password = ref('')
async function submit() {
await signIn.mutateAsync({
providerId: 'password',
input: { email: email.value, password: password.value },
})
}
</script>
<template>
<form @submit.prevent="submit">
<input v-model="email" type="email" required />
<input v-model="password" type="password" required />
<button :disabled="signIn.isLoading.value">
{{ signIn.isLoading.value ? 'Signing in...' : 'Sign in' }}
</button>
<p v-if="signIn.error.value" class="text-red-500">
{{ signIn.error.value.code }}
</p>
</form>
</template>
The mutate / mutateAsync / isLoading / error shape is the same
as React's. The values are refs; unwrap with .value outside of
templates.
SSR (Nuxt)
plugins/auth.ts
import { defineNuxtPlugin } from '#app'
import { createAuth } from '@gentleduck/auth/client/vue'
export default defineNuxtPlugin((nuxtApp) => {
const auth = createAuth({
baseUrl: '/api/auth',
noInitialFetch: !!nuxtApp.payload.serverRendered,
})
nuxtApp.vueApp.use(auth)
})
For hydration, set noInitialFetch: true on the server side and pass
the initial state via Nuxt payload:
// server middleware
const ctx = await auth.resolveSession(req)
nuxtApp.payload.authInitialState = ctx ? { session: ctx.session, identity: ctx.identity } : null
// plugin
createAuth({
noInitialFetch: true,
initialState: nuxtApp.payload.authInitialState,
})
Errors
<script setup>
const signIn = authUseSignIn()
</script>
<template>
<template v-if="signIn.error.value">
<p v-if="signIn.error.value.code === 'AUTH/INVALID_CREDENTIALS'">
Wrong email or password
</p>
<p v-else-if="signIn.error.value.code === 'AUTH/MFA_REQUIRED'">
<MfaPrompt />
</p>
<p v-else>
Something went wrong: {{ signIn.error.value.code }}
</p>
</template>
</template>