CloudFront Functions 中的 CWT 支持 - Amazon CloudFront
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

CloudFront Functions 中的 CWT 支持

此部分详细介绍 CloudFront Functions 中的 CBOR Web 令牌(CWT)支持功能,可使用该功能在 CloudFront 边缘站点实施基于令牌的安全身份验证和授权。此支持功能以模块形式提供,可通过 CloudFront 函数访问。

要使用此模块,请使用 JavaScript 运行时 2.0 创建 CloudFront 函数,并在函数代码的第一行包含以下语句:

import cf from 'cloudfront';

可通过以下方式访问与此模块关联的方法(其中,* 是表示模块中存在的不同函数的通配符):

cf.cwt.*

有关更多信息,请参阅适用于 CloudFront Functions 的 JavaScript 运行时系统 2.0 特征

目前,此模块仅支持采用 HS256(HMAC-SHA256)算法的 MAC0 结构,最大令牌大小的限制为 1 KB。

令牌结构

此部分介绍 CWT 模块所需的令牌结构。该模块要求令牌必须带有正确的标签且可识别(例如,COSE MAC0)。此外,在令牌结构方面,该模块遵循 CBOR 对象签名与加密(COSE)[RFC 8152] 设定的标准。

( // CWT Tag (Tag value: 61) --- optional ( // COSE MAC0 Structure Tag (Tag value: 17) --- required [ protectedHeaders, unprotectedHeaders, payload, tag, ] ) )
例 :使用 COSE MAC0 结构的 CWT
61( // CWT tag 17( // COSE_MAC0 tag [ { // Protected Headers 1: 4 // algorithm : HMAC-256-64 }, { // Unprotected Headers 4: h'53796d6d6574726963323536' // kid : Symmetric key id }, { // Payload 1: "https://iss.example.com", // iss 2: "exampleUser", // sub 3: "https://aud.example.com", // aud 4: 1444064944, // exp 5: 1443944944, // nbf 6: 1443944944, // iat }, h'093101ef6d789200' // tag ] ) )
注意

生成令牌时,CWT 标签是可选的。不过,COSE 结构标签是必需的。

validateToken() 方法

该函数使用指定密钥对 CWT 令牌进行解码和验证。如果验证成功,则将返回已解码的 CWT 令牌。否则,将引发错误。请注意,该函数不执行任何声明集验证操作。

请求

cf.cwt.validateToken(token, handlerContext{key})
参数
token(必需)

用于验证的编码令牌。这必须是 JavaScript 缓冲区。

handlerContext(必需)

一个 JavaScript 对象,用于存储 validateToken 调用的上下文。目前,仅支持 key 属性。

key(必需)

用于消息摘要计算的私密密钥。可以字符串或 JavaScript 缓冲区的形式提供。

响应

validateToken() 方法返回成功验证的令牌时,来自函数的响应将为采用以下格式的 CWTObject。解码后,所有声明密钥都以字符串形式表示。

CWTObject { protectedHeaders, unprotectedHeaders, payload }

示例:使用作为令牌一部分发送的 kid 验证令牌

此示例演示了 CWT 令牌验证过程,其中会从标头中提取 kid。之后,将 kid 传入 CloudFront Functions KeyValueStore 中,以提取用于验证令牌的私密密钥。

import cf from 'cloudfront' const CwtClaims = { iss: 1, aud: 3, exp: 4 } async function handler(event) { try { let request = event.request; let encodedToken = request.headers['x-cwt-token'].value; let kid = request.headers['x-cwt-kid'].value; // Retrieve the secret key from the kvs let secretKey = await cf.kvs().get(kid); // Now you can use the secretKey to decode & validate the token. let tokenBuffer = Buffer.from(encodedToken, 'base64url'); let handlerContext = { key: secretKey, } try { let cwtObj = cf.cwt.validateToken(tokenBuffer, handlerContext); // Check if token is expired const currentTime = Math.floor(Date.now() / 1000); // Current time in seconds if (cwtObj[CwtClaims.exp] && cwtObj[CwtClaims.exp] < currentTime) { return { statusCode: 401, statusDescription: 'Token expired' }; } } catch (error) { return { statusCode: 401, statusDescription: 'Invalid token' }; } } catch (error) { return { statusCode: 402, statusDescription: 'Token processing failed' }; } return request; }

generateToken() 方法

此函数使用提供的有效载荷和上下文设置来生成新的 CWT 令牌。

请求

cf.cwt.generateToken(generatorContext, payload)
参数
generatorContext(必需)

这是一个 JavaScript 对象,用作生成令牌的上下文且包含以下键值对:

cwtTag(可选)

此值是一个布尔值,如果为 true,则指定应添加 cwtTag

coseTag(必需)

指定 COSE 标签类型。目前仅支持 MAC0

key(必需)

用于计算消息摘要的私密密钥。此值可以是字符串或 JavaScript Buffer

payload(必需)

用于编码的令牌有效载荷。有效载荷必须采用 CWTObject 格式。

响应

返回包含已编码的令牌的 JavaScript 缓冲区。

例 :生成 CWT 令牌
import cf from 'cloudfront'; const CwtClaims = { iss: 1, sub: 2, exp: 4 }; const CatClaims = { catu: 401, catnip: 402, catm: 403, catr: 404 }; const Catu = { host: 1, path: 2, ext: 3 }; const CatuMatchTypes = { prefix_match: 1, suffix_match: 2, exact_match: 3 }; const Catr = { renewal_method: 1, next_renewal_time: 2, max_uses: 3 }; async function handler(event) { try { const response = { statusCode: 200, statusDescription: 'OK', headers: {} }; const commonAccessToken = { protected: { 1: "5", }, unprotected: {}, payload: { [CwtClaims.iss]: "cloudfront-documentation", [CwtClaims.sub]: "cwt-support-on-cloudfront-functions", [CwtClaims.exp]: 1740000000, [CatClaims.catu]: { [Catu.host]: { [CatuMatchTypes.suffix_match]: ".cloudfront.net" }, [Catu.path]: { [CatuMatchTypes.prefix_match]: "/media/live-stream/cf-4k/" }, [Catu.ext]: { [CatuMatchTypes.exact_match]: [ ".m3u8", ".ts", ".mpd" ] } }, [CatClaims.catnip]: [ "[IP_ADDRESS]", "[IP_ADDRESS]" ], [CatClaims.catm]: [ "GET", "HEAD" ], [CatClaims.catr]: { [Catr.renewal_method]: "header_renewal", [Catr.next_renewal_time]: 1750000000, [Catr.max_uses]: 5 } } }; if (!request.headers['x-cwt-kid']) { throw new Error('Missing x-cwt-kid header'); } const kid = request.headers['x-cwt-kid'].value; const secretKey = await cf.kvs().get(kid); if (!secretKey) { throw new Error('Secret key not found for provided kid'); } try { const genContext = { cwtTag: true, coseTag: "MAC0", key: secretKey }; const tokenBuffer = cf.cwt.generateToken(commonAccessToken, genContext); response.headers['x-generated-cwt-token'] = { value: tokenBuffer.toString('base64url') }; return response; } catch (tokenError) { return { statusCode: 401, statusDescription: 'Could not generate the token' }; } } catch (error) { return { statusCode: 402, statusDescription: 'Token processing failed' }; } }
例 :根据某种逻辑刷新令牌
import cf from 'cloudfront' const CwtClaims = { iss: 1, aud: 3, exp: 4 } async function handler(event) { try { let request = event.request; let encodedToken = request.headers['x-cwt-token'].value; let kid = request.headers['x-cwt-kid'].value; let secretKey = await cf.kvs().get(kid); // Retrieve the secret key from the kvs // Now you can use the secretKey to decode & validate the token. let tokenBuffer = Buffer.from(encodedToken, 'base64url'); let handlerContext = { key: secretKey, } try { let cwtJSON = cf.cwt.validateToken(tokenBuffer, handlerContext); // Check if token is expired const currentTime = Math.floor(Date.now() / 1000); // Current time in seconds if (cwtJSON[CwtClaims.exp] && cwtJSON[CwtClaims.exp] < currentTime) { // We can regnerate the token and add 8 hours to the expiry time cwtJSON[CwtClaims.exp] = Math.floor(Date.now() / 1000) + (8 * 60 * 60); let genContext = { coseTag: "MAC0", key: secretKey } let newTokenBuffer = cf.cwt.generateToken(cwtJSON, genContext); request.headers['x-cwt-regenerated-token'] = newTokenBuffer.toString('base64url'); } } catch (error) { return { statusCode: 401, statusDescription: 'Invalid token' }; } } catch (error) { return { statusCode: 402, statusDescription: 'Token processing failed' }; } return request; }