CloudFront Functions の CWT サポート
このセクションでは、CloudFront Functions での CBOR ウェブトークン (CWT) のサポートについて詳しく説明します。これにより、CloudFront Edge Locations での安全なトークンベースの認証と承認が可能になります。このサポートはモジュールとして提供され、CloudFront Functions でアクセスできます。
このモジュールを使用するには、JavaScript ランタイム 2.0 を使用して CloudFront Functions を作成し、関数コードの最初の行に次のステートメントを含めます。
import cf from 'cloudfront';
このモジュールに関連付けられたメソッドには、次からアクセスできます (* はモジュール内に存在するさまざまな関数を表すワイルドカードです)。
cf.cwt.*
詳細については、「CloudFront Functions の JavaScript ランタイム 2.0 の機能」を参照してください。
現在、モジュールは HS256 (HMAC-SHA256) アルゴリズムを使用した MAC0 構造のみをサポートし、トークンの最大サイズは 1KB に制限されています。
トークン構造
このセクションでは、CWT モジュールで想定されるトークン構造について説明します。モジュールは、トークンが正しくタグ付けされ、識別可能であること (COSE MAC0 など) を想定しています。さらに、トークンの構造については、モジュールは CBOR Object Signing and Encryption (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})
パラメータ
- トークン (必須)
-
検証用のエンコードされたトークン。これは JavaScript バッファである必要があります。
- handlerContext (必須)
-
validateToken 呼び出しのコンテキストを保存する JavaScript オブジェクト。現在、 キープロパティのみがサポートされています。
- キー (必須)
-
メッセージダイジェストの計算のシークレットキー。文字列または JavaScript バッファのいずれかとして指定できます。
応答
validateToken() メソッドが正常に検証されたトークンを返すと、関数からのレスポンスは次の形式の CWTObject になります。デコードされると、すべてのクレームキーが文字列として表されます。
CWTObject { protectedHeaders, unprotectedHeaders, payload }
例 – トークンの一部として送信された kid を使用してトークンを検証します
この例では、kid がヘッダーから抽出される CWT トークンの検証を示しています。その後、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のみサポートされます。 - キー (必須)
-
メッセージダイジェストを計算するシークレットキー。この値は、文字列または JavaScript
Bufferのいずれかです。
- ペイロード (必須)
-
エンコード用のトークンペイロード。ペイロードは
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; }