

# CloudFront Functions の CWT サポート
<a name="cwt-support-cloudfront-functions"></a>

このセクションでは、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 の機能](functions-javascript-runtime-20.md)」を参照してください。

現在、モジュールは HS256 (HMAC-SHA256) アルゴリズムを使用した MAC0 構造のみをサポートし、トークンの最大サイズは 1KB に制限されています。

## トークン構造
<a name="token-structure"></a>

このセクションでは、CWT モジュールで想定されるトークン構造について説明します。モジュールは、トークンが正しくタグ付けされ、識別可能であること (COSE MAC0 など) を想定しています。さらに、トークンの構造については、モジュールは [CBOR Object Signing and Encryption (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})
```パラメータ

**トークン (必須)**  
検証用のエンコードされたトークン。これは JavaScript バッファである必要があります。

**handlerContext (必須)**  
validateToken 呼び出しのコンテキストを保存する JavaScript オブジェクト。現在、 キープロパティのみがサポートされています。

**キー (必須)**  
メッセージダイジェストの計算のシークレットキー。文字列または JavaScript バッファのいずれかとして指定できます。

### 応答
<a name="validatetoken-response"></a>

`validateToken()` メソッドが正常に検証されたトークンを返すと、関数からのレスポンスは次の形式の `CWTObject` になります。デコードされると、すべてのクレームキーが文字列として表されます。

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

### 例 – トークンの一部として送信された kid を使用してトークンを検証します
<a name="validatetoken-example"></a>

この例では、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() メソッド
<a name="generatetoken-method"></a>

この関数は、提供されたペイロードとコンテキスト設定を使用して新しい CWT トークンを生成します。

### リクエスト
<a name="generatetoken-request"></a>

```
cf.cwt.generateToken(generatorContext, payload)
```パラメータ

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