

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# CloudFront Functions 的 CWT 支援
<a name="cwt-support-cloudfront-functions"></a>

本節提供 CloudFront Functions 中 CBOR Web Token (CWT) 支援的詳細資訊，可在 CloudFront Edge Locations 啟用安全字符型身分驗證和授權。此支援是以模組形式提供，可在 CloudFront Function 中存取。

若要使用此模組，請使用 JavaScript 執行期 2.0 建立 CloudFront 函數，並在函數程式碼的第一行中包含下列陳述式：

```
import cf from 'cloudfront';
```

與此模組相關聯的方法可透過 存取 （其中 \$1 是萬用字元，代表模組中存在的不同函數）：

```
cf.cwt.*
```

如需詳細資訊，請參閱[適用於 CloudFront Functions 的 JavaScript 執行時期 2.0 功能](functions-javascript-runtime-20.md)。

目前，模組僅支援具有 HS256 (HMAC-SHA256) 演算法的 MAC0 結構，字符大小上限為 1KB。

## 字符結構
<a name="token-structure"></a>

本節涵蓋 CWT 模組預期的字符結構。模組預期字符會正確標記和識別 （例如 COSE MAC0)。此外，對於字符的結構，模組遵循 [CBOR 物件簽署和加密 (COSE) 【RFC 8152】](https://datatracker.ietf.org/doc/html/rfc8152) 設定的標準。

```
( // CWT Tag (Tag value: 61) --- optional    
    ( // COSE MAC0 Structure Tag (Tag value: 17) --- required        
        [            
            protectedHeaders,            
            unprotectedHeaders,            
            payload,            
            tag,        
        ]    
    )
)
```

**Example ：使用 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() 方法
<a name="validatetoken-method"></a>

函數會使用指定的金鑰解碼和驗證 CWT 字符。如果驗證成功，則會傳回解碼的 CWT 字符。否則，它會擲回錯誤。請注意，此函數不會對宣告集進行驗證。

### 請求
<a name="validatetoken-request"></a>

```
cf.cwt.validateToken(token, handlerContext{key})
```Parameters

**字符 （必要）**  
用於驗證的編碼字符。這必須是 JavaScript 緩衝區。

**handlerContext （必要）**  
存放 validateToken 呼叫內容的 JavaScript 物件。目前僅支援 金鑰屬性。

**金鑰 （必要）**  
訊息摘要運算的私密金鑰。可做為字串或 JavaScript 緩衝區提供。

### 回應
<a name="validatetoken-response"></a>

當 `validateToken()`方法傳回成功驗證的字符時，函數的回應會以下列格式`CWTObject`顯示 。解碼後，所有宣告金鑰都會以字串表示。

```
CWTObject {    
    protectedHeaders,    
    unprotectedHeaders,    
    payload
}
```

### 範例 - 使用作為字符一部分傳送的 kid 驗證字符
<a name="validatetoken-example"></a>

此範例示範 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() 方法
<a name="generatetoken-method"></a>

此函數會使用提供的承載和內容設定產生新的 CWT 字符。

### 請求
<a name="generatetoken-request"></a>

```
cf.cwt.generateToken(generatorContext, payload)
```Parameters

**generatorContext （必要）**  
這是 JavaScript 物件，可做為產生字符的內容，並包含下列索引鍵值對：    
**cwtTag （選用）**  
此值是布林值，如果 `true`指定`cwtTag`應新增 。  
**coseTag （必要）**  
指定 COSE 標籤類型。目前僅支援 `MAC0`。  
**金鑰 （必要）**  
用於運算訊息摘要的私密金鑰。此值可以是字串或 JavaScript `Buffer`。

**承載 （必要）**  
用於編碼的字符承載。承載必須是 `CWTObject` 格式。

### 回應
<a name="generatetoken-response"></a>

傳回包含編碼字符的 JavaScript 緩衝區。

**Example ：產生 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'
        };
    }
}
```

**Example ：根據某些邏輯重新整理權杖**  

```
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;
}
```