useMagicLink

Vue composable that reads magic link query parameters from the current route, validates them with the gateway, and routes the verification call to the matching server endpoint based on the reason field.

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>
ParameterTypeDescription
pathstringOptional 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.

ParameterDescription
tokenTemporary verification token issued by the IAM service
randomCryptographic hash binding the token to the session
reasonThe flow reason, used to select the verification endpoint
visitorThe 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 reasonEndpoint called
magic_link_mfa_checksGET /api/auth/verify-mfa
password_resetGET /api/auth/reset-password
change_emailGET /api/auth/update-email
Any other valueThe 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, or visitor is missing from the route query
  • The reason does not match a built-in flow and no path argument was provided
  • The server endpoint returns an error or a NotFoundPath response
  • useAsyncData returns 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:

app/pages/auth/verify.vue
<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:

app/pages/auth/verify.vue
<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.

Logo