useMagicLink
useMagicLink is the composable that powers magic link verification pages. It reads the link query parameters, validates them, picks the correct server endpoint from the reason field, and calls executeRequest behind the scenes. The result is the full link metadata for your page to render.
import { useMagicLink } from 'auth-h3client/client'
const data = await useMagicLink()
Signature
function useMagicLink(path?: string): Promise<Data | NotFoundPath>
| Parameter | Type | Description |
|---|---|---|
path | string | Optional path to a custom verification endpoint. Used when the reason does not match any built-in flow. |
Query parameters read from the current route
The composable reads the following parameters from useRoute().query. If any are missing, it throws a 404 error that renders your Nuxt error page.
| Parameter | Description |
|---|---|
token | Temporary verification token issued by the IAM service |
random | Cryptographic hash binding the token to the session |
reason | The flow reason, used to select the verification endpoint |
visitor | The visitor fingerprint tied to the session that requested the link |
Routing by reason
The composable lowercases the reason and maps it to a server endpoint:
Lowercased reason | Endpoint called |
|---|---|
magic_link_mfa_checks | GET /api/auth/verify-mfa |
password_reset | GET /api/auth/reset-password |
change_email | GET /api/auth/update-email |
| Any other value | The path argument (404 if omitted) |
The comparison is case-insensitive, so a link carrying reason=MAGIC_LINK_MFA_CHECKS is routed to the same endpoint as one carrying reason=magic_link_mfa_checks. However its returned reason is not, and returned as is from the iam service (MAGIC_LINK_MFA_CHECKS).
Return value on success
interface Data {
token: string
random: string
visitor: string
reason: string
link: 'Password Reset' | 'MFA Code' | 'Custom MFA'
}
The link field identifies the high-level action category returned by the gateway after validating the link with the IAM service. Use it in your template to decide which form component to render.
Error handling
The composable throws a Nuxt 404 error in any of the following cases:
- Any of
token,random,reason, orvisitoris missing from the route query - The
reasondoes not match a built-in flow and nopathargument was provided - The server endpoint returns an error or a
NotFoundPathresponse useAsyncDatareturns an error or an empty value
Handle these by defining a Nuxt error page, or catch the error inside your page component.
Typical usage
Build a single verification page that handles every flow. Render the right form based on the reason:
<script setup lang="ts">
const data = await useMagicLink('/api/custom-verify')
</script>
<template>
<div v-if="data.reason.toLowerCase() === 'password_reset'">
<PasswordResetForm v-bind="data" />
</div>
<div v-else-if="data.reason.toLowerCase() === 'magic_link_mfa_checks'">
<MfaCodeForm v-bind="data" />
</div>
<div v-else-if="data.reason.toLowerCase() === 'change_email'">
<EmailChangeForm v-bind="data" />
</div>
<div v-else>
<CustomActionForm v-bind="data" />
</div>
</template>
Another example:
<script setup lang="ts">
import { type Component } from 'vue';
import AuthPasswordResetForm from '~/components/Auth/AuthPasswordResetForm.vue';
import AuthMfaForm from '~/components/Auth/AuthMfaForm.vue';
import AuthChangeEmail from '~/components/Auth/AuthChangeEmail.vue';
import {useMagicLink} from 'auth-h3client/client';
const result = await useMagicLink();
if ('error' in result) {
throw createError({
statusCode: 404,
statusText: 'Not Found',
message: `The page you are looking for doesn't exists`
});
}
const activeComponent = computed<Component | null>(() => {
switch (result.reason) {
case 'PASSWORD_RESET':
return AuthPasswordResetForm as Component;
case 'MAGIC_LINK_MFA_CHECKS':
return AuthMfaForm as Component;
case 'change_email':
return AuthChangeEmail as Component;
default:
return AuthMfaForm as Component;
}
});
const componentProps = computed(() => ({
...result
}));
</script>
<template>
<component
:is="activeComponent"
v-if="activeComponent"
v-bind="componentProps"
/>
</template>
Each form component posts the verification code back to the same server endpoint. See Client-Side MFA for the full end-to-end flow including the bounce route and code submission.