You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何向AWS Cognito createAuthChallenge触发器传递自定义数据

AWS Cognito自定义魔链登录传参问题解决方案

问题场景

使用AWS Cognito搭配AWS SES搭建自定义认证流程,实现魔链(Magic Link)登录:用户发起登录时,系统向用户邮箱发送携带令牌的登录链接。需求为根据用户来源自定义邮件内的跳转路径,例如部分用户跳转至https://mysite/foo?<token>,另一部分用户跳转至https://mysite/bar?<token>。
当前卡点为无法向Cognito认证触发器传递自定义变量。已配置CUSTOM_AUTH认证流,全程使用@aws-sdk/client-cognito-identity-provider原生SDK调用initiateAuthCommand发起认证请求,不希望引入Amplify框架。

现有实现代码

发起认证请求代码

import { CognitoIdentityProviderClient, InitiateAuthCommand } from '@aws-sdk/client-cognito-identity-provider'

const poolConfig = {
    accessKeyId: process.env.AWS_APIKEY,
    secretAccessKey: process.env.AWS_SECRETKEY,
    region: process.env.AWS_REGION,
    poolId: process.env.AWS_COGNITO_POOL_ID,
    poolClientId: process.env.AWS_COGNITO_POOL_CLIENT_ID
}

const provider = new CognitoIdentityProviderClient(poolConfig)

const authInput = {
    AuthFlow: "CUSTOM_AUTH",
    AuthParameters : {
        USERNAME: "test@mydomain.com",
    },
    ClientId: process.env.AWS_COGNITO_POOL_CLIENT_ID
}

const signinCommand = new InitiateAuthCommand(authInput)

try {
    const res = await provider.send(signinCommand)
    console.log('Signin success. Result: ', res)
} catch (e) {
    console.log('Signin fail. Error: ', e)
}

createAuthChallenge触发器代码

import { SESv2Client, SendEmailCommand } from "@aws-sdk/client-sesv2";
import { randomDigits } from 'crypto-secure-random-digit';

const config = {
    accessKeyId: process.env.AWS_APIKEY,
    secretAccessKey: process.env.AWS_SECRETKEY,
    region: process.env.AWS_REGION,
}

const client = new SESv2Client(config);

export const handler = async (event) => {

    let secretLoginCode;

    if (!event.request.session || !event.request.session.length) {

        secretLoginCode = randomDigits(10).join('');

        // 卡点:无法在此处获取传入的自定义变量(例如originUrl)

        await sendEmail(event.request.userAttributes.email, originUrl, secretLoginCode);

    } else {

        const previousChallenge = event.request.session.slice(-1)[0];
        secretLoginCode = previousChallenge.challengeMetadata.match(/CODE-(\d*)/)[1];
    }

    event.response.publicChallengeParameters = {
        email: event.request.userAttributes.email
    };

    event.response.privateChallengeParameters = { secretLoginCode };

    event.response.challengeMetadata = `CODE-${secretLoginCode}`;

    return event;
};

async function sendEmail(emailAddress, originUrl, secretLoginCode) {

    var params = {
        Destination: {
            ToAddresses: [
                emailAddress
            ]
        },
        Content: {
            Simple: {
                Body: {
                    Html: {
                        Charset: "UTF-8",
                        Data: `<p>Your link to connect is : <a href="${originUrl}/register?t=${secretLoginCode}">here</a></p>`
                    },
                },
                Subject: {
                    Charset: 'UTF-8',
                    Data: 'Test email'
                }
            }

        },
        FromEmailAddress: 'fromAddress@mydomain.com',
    };

    const command = new SendEmailCommand(params);

    await client.send(command);
}

已验证无效的方案

  • 开启应用客户端EnablePropagateAdditionalUserContextData参数:该参数用于自适应认证场景下传递设备指纹数据,必须开通Cognito高级安全付费功能才能使用,既无法独立完成账单审批,为传递简单自定义参数开通付费功能也不具备合理性。
  • 调用接口时传入ClientMetadata参数:InitiateAuth接口传入的ClientMetadata仅会传递给预注册、预认证、用户迁移类触发器,不会传递给自定义认证流的createAuthChallenge触发器,实测无法取值。

可行实现方案

核心逻辑:利用自定义认证流的DefineAuthChallenge触发器做参数中转,将AuthParameters中传入的自定义参数通过会话内部的challengeMetadata字段流转到createAuthChallenge触发器,全程无需开通付费功能,无需引入额外框架。

  1. 修改发起认证请求的参数,将自定义字段直接加入AuthParameters对象:
const authInput = {
    AuthFlow: "CUSTOM_AUTH",
    AuthParameters : {
        USERNAME: "test@mydomain.com",
        originUrl: "https://mysite/foo" // 直接传入需要的自定义路径参数
    },
    ClientId: process.env.AWS_COGNITO_POOL_CLIENT_ID
}
  1. 新增/修改DefineAuthChallenge触发器,将请求中携带的自定义参数序列化后存入challengeMetadata,流转给后续触发器:
export const handler = async (event) => {
  // 首次发起认证无会话记录
  if (!event.request.session || event.request.session.length === 0) {
    event.response.challengeName = "CUSTOM_CHALLENGE";
    event.response.failAuthentication = false;
    event.response.issueTokens = false;
    // 从validationData中取出AuthParameters传入的自定义参数,序列化后存入challengeMetadata
    const customParams = encodeURIComponent(JSON.stringify({
      originUrl: event.request.validationData?.originUrl || "https://mysite/default"
    }))
    event.response.challengeMetadata = customParams;
  } else {
    // 校验挑战答案逻辑和原有逻辑保持一致
    const lastChallenge = event.request.session.slice(-1)[0];
    event.response.issueTokens = lastChallenge.challengeResult === true;
    event.response.failAuthentication = lastChallenge.challengeResult !== true;
  }
  return event;
}
  1. 修改createAuthChallenge触发器,从传入的challengeMetadata中解析自定义参数,即可在发邮件时使用:
export const handler = async (event) => {
    let secretLoginCode;
    let originUrl = "https://mysite/default"; // 配置默认兜底路径

    if (!event.request.session || !event.request.session.length) {
        secretLoginCode = randomDigits(10).join('');
        // 从上游传过来的challengeMetadata解析自定义参数
        if (event.request.challengeMetadata) {
          const customParams = JSON.parse(decodeURIComponent(event.request.challengeMetadata));
          originUrl = customParams.originUrl;
        }
        await sendEmail(event.request.userAttributes.email, originUrl, secretLoginCode);
    } else {
        const previousChallenge = event.request.session.slice(-1)[0];
        secretLoginCode = previousChallenge.challengeMetadata.match(/CODE-(\d*)/)[1];
    }

    event.response.publicChallengeParameters = {
        email: event.request.userAttributes.email
    };

    event.response.privateChallengeParameters = { secretLoginCode };
    event.response.challengeMetadata = `CODE-${secretLoginCode}`;

    return event;
};

注意事项

  • challengeMetadata为Cognito会话内部流转字段,不会暴露给前端,可安全存储跳转路径这类非敏感数据,不要存入密钥类敏感信息。
  • publicChallengeParameters会直接返回给前端调用方,不要存放敏感数据。

内容的提问来源于stack exchange,提问作者antoine david

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 00:18:37