

# CloudFront Functions에 대한 CWT 지원
<a name="cwt-support-cloudfront-functions"></a>

이 섹션에서는 CloudFront 엣지 로케이션에서 보안 토큰 기반 인증 및 권한 부여를 활성화하는 CloudFront Functions의 CBOR 웹 토큰(CWT) 지원에 대한 세부 정보를 제공합니다. 이 지원은 CloudFront Functions에서 액세스할 수 있는 모듈로 제공됩니다.

이 모듈을 사용하려면 JavaScript 런타임 2.0을 사용하여 CloudFront Functions을 만들고 함수 코드의 첫 번째 줄에 다음 스테이트먼트를 포함합니다.

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

이 모듈과 연결된 메서드는 다음을 통해 액세스할 수 있습니다(여기서 \*는 모듈에 있는 다양한 함수를 나타내는 와일드카드).

```
cf.cwt.*
```

자세한 내용은 [CloudFront Functions를 위한 JavaScript 런타임 2.0 기능](functions-javascript-runtime-20.md) 섹션을 참조하세요.

현재 모듈은 최대 토큰 크기에 대해 한도가 1KB인 HS256(HMAC-SHA256) 알고리즘을 사용하는 MAC0 구조만 지원합니다.

## 토큰 구조
<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 토큰 검증을 보여줍니다. 그런 다음 키드가 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;
}
```