ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Serverless Framework 集成测试的 Cognito 前置依赖:为 MCP 鉴权与发现测试套件预置一次性的 M2M 用户池

Serverless Framework 集成测试的 Cognito 前置依赖:为 MCP 鉴权与发现测试套件预置一次性的 M2M 用户池 Serverless Framework 集成测试的 Cognito 前置依赖为 MCP 鉴权与发现测试套件预置一次性的 M2M 用户池【免费下载链接】serverless⚡ Serverless Framework – Effortlessly build apps that auto-scale, incur zero costs when idle, and require minimal maintenance using AWS Lambda and other managed cloud services.项目地址: https://gitcode.com/GitHub_Trending/se/serverless本指南以 mcp-cognito-prerequisite/README.md 为骨架深入讲解 Serverless Frameworksf-core在真实 AWS 集成测试中如何用一个一次性、持久、按账户部署的 Cognito 用户池来支撑 MCP 属性的 enforcement-and-discovery 测试套件包括模板预置了什么资源、为什么必须用自定义资源把密钥写成 SSM SecureString、部署/校验/拆除的命令以及测试套件如何从 SSM 运行时发现一切、做到零硬编码、无前置则干净跳过、读取出错则响亮失败。读完后你可以复现这套部署流程并理解client_credentialsM2M 鉴权测试中 issuer、token endpoint、scope 与资源服务器之间的真实关系。这套前置依赖在项目中的定位Serverless Framework 的mcp属性本身不做鉴权——这是 mcp-auth.test.js 开头就点明的核心立场Enforcement is the users执行鉴权是用户的责任。框架只负责把用户配置的 authorizer 编译成 API Gateway 形态并可选地在 API Gateway MOCK 路由上发布一份 RFC 9728 protected-resource 文档供交互式 MCP 客户端发现登录入口。为了在真实 AWS 上证明这一立场测试套件需要部署四类受不同访问控制保护的 MCP 服务器。其中cognito这一类需要一个真实存在的 Cognito 用户池做 API Gateway Cognito authorizer 的授权主体。由于用户池 id、域名、客户端密钥等在编写测试时无法预先知道且不应硬编码进仓库项目采用了一个独立于测试套件、每次运行自生自灭的 fixture 栈之外的持久化前置依赖即本目录中的template.yml预置的 Cognito 用户池。按 TESTING.md 的编排只有mcp-auth.test.jsenforcement-and-discovery 套件需要这个 Cognito 前置依赖缺少它时该套件会打印日志后跳过其余 MCP 套件如mcp.test.js不受影响。设计原则一次性部署、持久存在、运行时发现前置依赖设计上有三条硬约束持久且按账户一次性部署用纯 CloudFormation 部署一次后长期保留测试套件在运行时从 SSM 读取全部标识因此没有硬编码任何 id。共享同一 fixture 目录在 jest 并行下是竞态因此本前置与套件分离套件自己的 fixture 位于 fixture-auth每套测试独占一个目录。缺失时干净跳过、损坏时响亮失败套件只在 SSM 前缀真正不可用空、不完整或完全没有凭证时跳过被拒绝、被限流或超时的读取会直接导致任务失败——那意味着 CI 环境坏了而不是账户主动退出测试。template.yml 预置了什么模板位于 template.yml是一个标准AWSTemplateFormatVersion: 2010-09-09的 CloudFormation 模板注意是纯 CloudFormation而非 Serverless 服务定义。它包含三个可覆盖的模板参数参数默认值说明SsmPrefix/mcp-integration-test/cognitoSSM 参数前缀池 id、域名、region、两个客户端 id/secret 与 scope 都写在此前缀之下必须与 cognito.mjs 中导出的DEFAULT_PREFIX保持一致ResourceServerIdentifiermcp资源服务器标识与ScopeName拼接成完整 scopeScopeNameinvoke自定义 scope 名完整 scope 字符串形如identifier/name其预置的核心资源包括一个 Lite 层的用户池AWS::Cognito::UserPoolUserPoolName: mcp-integration-test。Lite 层足够的原因在模板注释中写得很清楚套件部署的受保护服务器配置了自定义 scope正是 scope 让 API Gateway 校验该池签发的原始 access token因此无需 token 定制/pre-token-generation 触发器。一个用户池域名mcp-integration-test-account-idAWS::Cognito::UserPoolDomain。域名按账户 id 命名以保证全局唯一同时它承载/oauth2/token端点套件正是用它铸币minttoken。一个资源服务器mcpAWS::Cognito::UserPoolResourceServer带一个自定义 scopeinvoke→ 完整 scope 字符串mcp/invokescope 描述为 Invoke the MCP server。两个应用客户端 Client A / Client BAWS::Cognito::UserPoolClient均为client_credentialsM2M 客户端GenerateSecret: true由 CloudFormation 生成客户端密钥AllowedOAuthFlows: [client_credentials]AllowedOAuthScopes限定为mcp/invokeSupportedIdentityProviders: [COGNITO]二者都DependsOn: ResourceServer确保 scope 先于客户端创建。两个客户端的角色差异是这套测试的考点之一Client A套件铸造工作 tokenworking token的客户端。Client B同一池、同一 scope但不同 client id。测试套件断言它的 token同样被接受——因为 API Gateway 的 Cognito authorizer 的授权粒度是池 scope永远不是某一个客户端。固定这一个断言就能阻止读者误以为网关在客户端层面做了更细的收敛。真正要收窄到单个客户端是服务器模块的职责token 的client_id声明会随 authorizer context 到达服务器这恰好印证了enforcement is yours的边界见 mcp-auth.test.js。密钥如何进 SSM为什么必须自定义资源CloudFormation 原生的AWS::SSM::Parameter无法创建 SecureString 类型参数——这是模板注释和 README 共同强调的限制。解决办法是模板内联ZipFile了一个极小的 Python 3.12 Lambda 自定义资源SsmWriterFunction栈名派生为stack-name-ssm-writer。它发布的八个 SecureString 参数位于/mcp-integration-test/cognito/下poolId、domain、region、clientAId、clientASecret、clientBId、clientBSecret、scope。这八个键名与 cognito.mjs 里的REQUIRED_KEYS逐一对应——八个必须全部存在前置依赖才视为已部署。几个值得注意的安全与生命周期细节客户端密钥只存在于 SSM从不进入栈的 Outputs。模板的 Outputs 只回显非敏感发现值UserPoolId、Domain、Region、ClientAId、ClientBId、Scope、SsmPrefix供管理员部署后目测。自定义资源的 IAM 最小化SsmWriterRole只允许ssm:PutParameter/ssm:DeleteParameter且资源被限定为arn:...:parameter${SsmPrefix}/*日志写权限只针对该函数自己显式声明的日志组/aws/lambda/${AWS::StackName}-ssm-writerRetentionInDays: 7。因为日志组是显式声明的而非首次调用时自动创建函数名由栈名派生在创建角色时就可确定不存在依赖环也完全不需要logs:CreateLogGroup。删除即清理自定义资源在RequestType Delete时逐个delete_parameter对ParameterNotFound静默容错Create/Update则用Overwrite: True覆盖写入。因此拆除整个栈就会连带清掉八个 SSM 参数不会遗留凭据。密钥通过!GetAtt UserPoolClientA.ClientSecret/!GetAtt UserPoolClientB.ClientSecret从客户端资源取回随模板传入 Lambda 的属性properties全程不落明文 Outputs。两个主机切勿混淆README 与 cognito.mjs 都强调同一件事M2M 流程里有两个不同角色、不同域名的主机Issuer签发方标识https://cognito-idp.region.amazonaws.com/poolId——套件将其发布到 fixture 的oauthDiscovery.issuer字段客户端读取受保护资源文档后被告知去这里登录。Token endpoint铸币端点https://domain.auth.region.amazoncognito.com/oauth2/token——套件用它铸造 access token。而 authorizer 实际引用的pool ARN 并不在此发布套件在运行时由poolId、region和调用方自己的账户 id 推导见 mcp-auth.test.js从 STSGetCallerIdentity取 partition 与 account id拼出arn:partition:cognito-idp:region:account:userpool/poolId。派生而非硬编码避免了跨分区partition假设。套件如何消费前置依赖skip 与 fail 的边界读取逻辑集中在 cognito.mjs 的readCognitoPrerequisite它在**收集期collection time**执行一次让套件在运行前就能决定是否可跑用GetParametersByPathCommandWithDecryption: true、自动翻页读取前缀下的全部参数命中SKIPPABLE_ERROR_NAMES一个白名单ParameterNotFound表示前缀不存在CredentialsProviderError表示凭证链完全拿不到凭证时才返回null→ 套件describe.skip其余任何失败都会向上抛出让文件响亮失败。设计上刻意不用.catch兜底被拒绝、限流、过期凭证、网络超时都是本该成功的读取静默跳过会让鉴权覆盖在什么都没跑的情况下被记为已覆盖这是套件存在的意义所不允许的。成功时返回的对象携带八个原始值以及两个派生工具issuer、tokenEndpoint外加零参数的铸币器mintClientA()/mintClientB()。铸币走 mintToken对 token endpoint POSTgrant_typeclient_credentialsscopemcp/invokeHTTP Basic 认证仅用全局fetch因此请求形态可被单元测试用 stub 的 fetch 验证无需真实网络与真实池对应测试在 cognito.test.js。读取方套件侧在 mcp-auth.test.js 打印明确的跳过警告指引开发者先部署前置模板。部署每个账户执行一次模板所在目录持有template.yml会让serverless deploy路由到框架的CloudFormation runner由后者自行传递 IAM capabilities。从packages/sf-core/tests/integration/mcp-cognito-prerequisite/目录执行serverless deploy --stack mcp-integration-test-cognito --region us-east-1在已有该栈的账户上重复执行是安全的会用上面命名的SsmWriterFunction替换写入函数并重写同样的八个参数。需要留意的一个小尾巴早期版本创建过自动命名的日志组/aws/lambda/stack-SsmWriterFunction-*栈不会接管它若在意可手动删除。成本几乎为零Cognito 按 M2M 客户端收取的费用已于 2025 年 11 月取消剩余成本为每 1000 次 token 请求 $0.00225即便按每月 1000 次 CI 运行计也约$0.014/月闲置池在约 0 MAU 下为 $0。校验与排查部署后可以用两类命令确认状态# 查看栈输出非敏感发现值 serverless info --stack mcp-integration-test-cognito --region us-east-1 # 确认八个 SecureString 参数存在--query 只列名称不回显密钥值 aws ssm get-parameters-by-path --path /mcp-integration-test/cognito \ --with-decryption --region us-east-1 --query Parameters[].Name拆除与迁移要把前置依赖迁到另一个账户例如专门的 CI 账户做法是在目标账户部署同一模板、在此账户拆除serverless remove --stack mcp-integration-test-cognito --region us-east-1删除栈时自定义资源会在 Delete 阶段清掉八个 SSM 参数无需额外的手工清理步骤。前置依赖之外的验证全景虽然本前置只是跑腿资源但它支撑的测试断言才是理解其存在意义的关键见 mcp-auth.test.js 的四个 describe 块cognito垃圾 token 被网关以 401 拒绝且从 CloudWatch 计数证明服务器函数从未被调用countInvocations按REPORT RequestId过滤整页扫描拒绝与接受互为对照用 Client A 的 token 跑完整 MCP 检查清单再用 Client B 的 token 验证同池同 scope 的任意客户端都被接受。custom / customRequestTOKEN 与 REQUEST 两类 Lambda authorizer 形态校验每次运行随机生成的共享密钥钉死网关拒绝时的精确响应体401 {message:Unauthorized}x-amzn-errortype: UnauthorizedException。oauthDiscoveryMOCK 路由上的 RFC 9728 受保护资源文档的逐字节内容与 CORS 头以及文档恰好能被服务器路由拒绝的那个未认证客户端读取这一关键性质同时断言裸 execute-api 端点上的根探测返回 403自定义域名映射到根路径时该探测才会解析。open无 authorizer 的普通 MCP 往返兼作流式传输与空 202 通知的回归门。四个服务器在 serverless-auth.yml 中定义指向同一份服务器模块与fixture/的副本由 fixture-parity.test.js 强制逐字节一致唯一变化的变量就是谁被允许到达它——这正是本前置依赖服务的验证目标。延伸阅读前置依赖模板与说明template.yml、README.md消费方套件与读取库mcp-auth.test.js、cognito.mjsfixture 配置serverless-auth.yml、fixture-auth/README.md测试编排总览TESTING.md含该前置依赖在整个集成测试矩阵中的位置与运行方式【免费下载链接】serverless⚡ Serverless Framework – Effortlessly build apps that auto-scale, incur zero costs when idle, and require minimal maintenance using AWS Lambda and other managed cloud services.项目地址: https://gitcode.com/GitHub_Trending/se/serverless创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表