CloudFront Functions에 대한 CWT 지원
이 섹션에서는 CloudFront 엣지 로케이션에서 보안 토큰 기반 인증 및 권한 부여를 활성화하는 CloudFront Functions의 CBOR 웹 토큰(CWT) 지원에 대한 세부 정보를 제공합니다. 이 지원은 CloudFront Functions에서 액세스할 수 있는 모듈로 제공됩니다.
이 모듈을 사용하려면 JavaScript 런타임 2.0을 사용하여 CloudFront Functions을 만들고 함수 코드의 첫 번째 줄에 다음 스테이트먼트를 포함합니다.
import cf from 'cloudfront';
이 모듈과 연결된 메서드는 다음을 통해 액세스할 수 있습니다(여기서 *는 모듈에 있는 다양한 함수를 나타내는 와일드카드).
cf.cwt.*
자세한 내용은 CloudFront Functions를 위한 JavaScript 런타임 2.0 기능 섹션을 참조하세요.
현재 모듈은 최대 토큰 크기에 대해 한도가 1KB인 HS256(HMAC-SHA256) 알고리즘을 사용하는 MAC0 구조만 지원합니다.
토큰 구조
이 섹션에서는 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})
Parameters
- 토큰(필수)
-
검증을 위한 인코딩된 토큰입니다. JavaScript 버퍼여야 합니다.
- handlerContext(필수)
-
validateToken 직접 호출에 대한 컨텍스트를 저장하는 JavaScript 객체입니다. 현재 키 속성만 지원됩니다.
- 키(필수)
-
메시지 다이제스트 계산을 위한 비밀 키입니다. 문자열 또는 JavaScript 버퍼로 제공할 수 있습니다.
응답
validateToken() 메서드가 성공적으로 검증된 토큰을 반환하면 함수의 응답은 다음 형식의 CWTObject입니다. 디코딩되면 모든 클레임 키가 문자열로 표시됩니다.
CWTObject { protectedHeaders, unprotectedHeaders, payload }
예제 - 토큰의 일부로 전송된 KID로 토큰 검증
이 예제는 키드가 헤더에서 추출되는 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() 메서드
이 함수는 제공된 페이로드 및 컨텍스트 설정을 사용하여 새 CWT 토큰을 생성합니다.
요청
cf.cwt.generateToken(generatorContext, payload)
Parameters
- 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; }