diff --git a/plugins/email/server/auth/email.ts b/plugins/email/server/auth/email.ts index a0e9a0266..612396d80 100644 --- a/plugins/email/server/auth/email.ts +++ b/plugins/email/server/auth/email.ts @@ -90,7 +90,15 @@ router.get( "email.callback", validate(T.EmailCallbackSchema), async (ctx: APIContext) => { - const { token, client } = ctx.input.query; + const { token, client, follow } = ctx.input.query; + + // The link in the email does not include the follow query param, this + // is to help prevent anti-virus, and email clients from pre-fetching the link + // and spending the token before the user clicks on it. Instead we redirect + // to the same URL with the follow query param added from the client side. + if (!follow) { + return ctx.redirectOnClient(ctx.request.href + "&follow=true"); + } let user!: User; diff --git a/plugins/email/server/auth/schema.ts b/plugins/email/server/auth/schema.ts index 700266ab7..9412f7dd9 100644 --- a/plugins/email/server/auth/schema.ts +++ b/plugins/email/server/auth/schema.ts @@ -15,6 +15,7 @@ export const EmailCallbackSchema = BaseSchema.extend({ query: z.object({ token: z.string(), client: z.nativeEnum(Client).default(Client.Web), + follow: z.string().default(""), }), });