API 보안 설정
API 보안 설정
이제 사용자 풀과 Identity Pool 및 인증 역할을 생성했으므로, 이를 사용해 API 접근을 보호할 준비가 되었습니다.
Serverless IAM 인증
먼저 serverless.yml
파일의 functions:
블록을 수정해 보겠습니다.
functions:
# create.js의 main 함수를 호출하는 HTTP API 엔드포인트 정의
# - path: URL 경로는 /notes
# - method: POST 요청
# - authorizer: AWS IAM 역할을 사용하여 인증
create:
handler: create.main
events:
- http:
path: notes
method: post
authorizer: aws_iam
get:
# get.js의 main 함수를 호출하는 HTTP API 엔드포인트 정의
# - path: URL 경로는 /notes/{id}
# - method: GET 요청
handler: get.main
events:
- http:
path: notes/{id}
method: get
authorizer: aws_iam
list:
# list.js의 main 함수를 호출하는 HTTP API 엔드포인트 정의
# - path: URL 경로는 /notes
# - method: GET 요청
handler: list.main
events:
- http:
path: notes
method: get
authorizer: aws_iam
update:
# update.js의 main 함수를 호출하는 HTTP API 엔드포인트 정의
# - path: URL 경로는 /notes/{id}
# - method: PUT 요청
handler: update.main
events:
- http:
path: notes/{id}
method: put
authorizer: aws_iam
delete:
# delete.js의 main 함수를 호출하는 HTTP API 엔드포인트 정의
# - path: URL 경로는 /notes/{id}
# - method: DELETE 요청
handler: delete.main
events:
- http:
path: notes/{id}
method: delete
authorizer: aws_iam
여기서 중요한 변경 사항은 각 함수에 다음 줄을 추가한 것입니다.
authorizer: aws_iam
이 줄은 Serverless Framework에게 우리의 API가 Identity Pool을 사용하여 보안되고 있음을 알려줍니다. 이 과정은 대략 다음과 같이 동작합니다:
- 서명된 인증 헤더가 포함된 요청이 API로 전송됩니다.
- AWS는 헤더를 사용하여 어떤 Identity Pool이 연결되어 있는지 확인합니다.
- Identity Pool은 요청이 우리의 User Pool로 인증된 사용자에 의해 서명되었는지 확인합니다.
- 그렇다면, 이 요청에 Auth IAM 역할을 할당합니다.
- 마지막으로, IAM은 이 역할이 우리의 API에 접근할 수 있는 권한이 있는지 확인합니다.
모든 과정이 성공적으로 완료되면, Lambda 함수가 호출됩니다. 그리고 함수 핸들러의 event
매개변수에는 API를 호출한 사용자에 대한 정보가 포함됩니다.
Cognito Identity Id
우리의 Lambda 함수 시그니처를 다시 떠올려 보겠습니다.
export async function main(event, context) {}
또는 현재 사용 중인 리팩토링된 버전:
export const main = handler(async (event, context) => {});
지금까지 event
객체를 사용하여 경로 매개변수(event.pathParameters
)와 요청 본문(event.body
)을 가져왔습니다.
이제 인증된 사용자의 ID를 가져오겠습니다.
event.requestContext.identity.cognitoIdentityId;
이 ID는 Cognito Identity Pool이 우리 사용자에게 할당한 ID입니다.
지금까지 모든 API가 단일 사용자(사용자 ID 123
)와 상호작용하도록 하드코딩되어 있음을 기억하실 겁니다.
userId: "123", // 작성자의 ID
이제 이를 변경해 보겠습니다.
create.js
에서 위의 줄을 다음과 같이 바꿉니다.
userId: event.requestContext.identity.cognitoIdentityId, // 작성자의 ID
get.js
에서도 동일하게 변경합니다.
userId: event.requestContext.identity.cognitoIdentityId, // 작성자의 ID
update.js
에서도 마찬가지로 변경합니다.
userId: event.requestContext.identity.cognitoIdentityId, // 작성자의 ID
delete.js
에서도 변경합니다.
userId: event.requestContext.identity.cognitoIdentityId, // 작성자의 ID
list.js
에서는 다음 줄을 찾습니다.
":userId": "123",
그리고 이를 다음과 같이 바꿉니다.
":userId": event.requestContext.identity.cognitoIdentityId,
위의 userId
는 Federated Identity ID(또는 Identity Pool 사용자 ID)임을 기억하세요. 이는 User Pool에서 할당된 사용자 ID가 아닙니다. User Pool 사용자 ID를 사용하고 싶다면, Cognito Identity Id와 User Pool Id 매핑 챕터를 참고하세요.
로컬에서 테스트하기
이전에 API 엔드포인트를 처음 생성했던 챕터를 떠올려보면, Lambda 함수를 테스트하기 위해 모의 이벤트(mock events)를 사용했습니다. 이 파일들은 mocks/
디렉토리에 저장되어 있습니다.
예를 들어, create-event.json
파일은 다음과 같습니다.
{
"body": "{\"content\":\"hello world\",\"attachment\":\"hello.jpg\"}"
}
이제 이 파일을 수정하여 event.requestContext.identity.cognitoIdentityId
를 전달해야 합니다. 이제 이를 수행해 보겠습니다.
create-event.json
을 다음과 같이 변경합니다.
{
"body": "{\"content\":\"hello world\",\"attachment\":\"hello.jpg\"}",
"requestContext": {
"identity": {
"cognitoIdentityId": "USER-SUB-1234"
}
}
}
여기서는 테스트 목적으로 cognitoIdentityId
에 더미 값을 전달합니다.
이제 프로젝트 루트에서 다음 명령어를 실행합니다.
$ serverless invoke local --function create --path mocks/create-event.json
테스트 사용자를 위한 새로운 노트 객체가 생성된 것을 확인할 수 있습니다.
{
"statusCode": 200,
"body": "{\"userId\":\"USER-SUB-1234\",\"noteId\":\"0101be80-18b9-11eb-893d-b7fc3f6c5167\",\"content\":\"hello world\",\"attachment\":\"hello.jpg\",\"createdAt\":1603846842984}"
}
이제 다른 모의 이벤트 파일도 업데이트합니다.
get-event.json
을 다음과 같이 변경합니다.
{
"pathParameters": {
"id": "cf6a83b0-1314-11eb-9506-9133509a950f"
},
"requestContext": {
"identity": {
"cognitoIdentityId": "USER-SUB-1234"
}
}
}
update-event.json
을 다음과 같이 변경합니다.
{
"body": "{\"content\":\"new world\",\"attachment\":\"new.jpg\"}",
"pathParameters": {
"id": "cf6a83b0-1314-11eb-9506-9133509a950f"
},
"requestContext": {
"identity": {
"cognitoIdentityId": "USER-SUB-1234"
}
}
}
delete-event.json
을 다음과 같이 변경합니다.
{
"pathParameters": {
"id": "a63c5450-1274-11eb-81db-b9d1e2c85f15"
},
"requestContext": {
"identity": {
"cognitoIdentityId": "USER-SUB-1234"
}
}
}
마지막으로, list-event.json
을 다음과 같이 변경합니다.
{
"requestContext": {
"identity": {
"cognitoIdentityId": "USER-SUB-1234"
}
}
}
이제 로컬에서 사용자와 연결된 Lambda 함수를 테스트할 수 있습니다.
변경 사항 배포
지금까지 만든 변경 사항을 빠르게 배포해 보겠습니다.
프로젝트 루트에서 다음 명령어를 실행하세요.
$ serverless deploy
배포가 완료되면 배포된 엔드포인트와 함수를 확인할 수 있습니다.
Service Information
service: notes-api
stage: prod
region: us-east-1
stack: notes-api-prod
resources: 32
api keys:
None
endpoints:
POST - https://0f7jby961h.execute-api.us-east-1.amazonaws.com/prod/notes
GET - https://0f7jby961h.execute-api.us-east-1.amazonaws.com/prod/notes/{id}
GET - https://0f7jby961h.execute-api.us-east-1.amazonaws.com/prod/notes
PUT - https://0f7jby961h.execute-api.us-east-1.amazonaws.com/prod/notes/{id}
DELETE - https://0f7jby961h.execute-api.us-east-1.amazonaws.com/prod/notes/{id}
functions:
create: notes-api-prod-create
get: notes-api-prod-get
list: notes-api-prod-list
update: notes-api-prod-update
delete: notes-api-prod-delete
layers:
None
다음으로, 새로 보안이 적용된 API를 테스트해 보겠습니다.
For help and discussion
Comments on this chapter