서버리스 앱에 인증 추가하기

지금까지 DynamoDB 테이블, S3 버킷, 그리고 API를 포함한 서버리스 백엔드를 만들었습니다. 이제 인증 기능을 추가해 보겠습니다. 이전 챕터에서 이야기했듯이, 사용자 가입과 로그인 관리를 위해 Cognito User Pool을 사용할 것입니다. 또한 사용자가 접근할 수 있는 리소스를 관리하기 위해 Cognito Identity Pool을 사용할 예정입니다.

이 모든 것을 Terraform으로 설정하는 것은 꽤 복잡할 수 있습니다. SST는 이를 도와주기 위해 간단한 CognitoUserPoolCognitoIdentityPool 컴포넌트를 제공합니다.

컴포넌트 생성하기

Change indicator infra/auth.ts 파일에 다음 내용을 추가하세요.

import { api } from "./api";
import { bucket } from "./storage";

const region = aws.getRegionOutput().name;

export const userPool = new sst.aws.CognitoUserPool("UserPool", {
  usernames: ["email"]
});

export const userPoolClient = userPool.addClient("UserPoolClient");

export const identityPool = new sst.aws.CognitoIdentityPool("IdentityPool", {
  userPools: [
    {
      userPool: userPool.id,
      client: userPoolClient.id,
    },
  ],
  permissions: {
    authenticated: [
      {
        actions: ["s3:*"],
        resources: [
          $concat(bucket.arn, "/private/${cognito-identity.amazonaws.com:sub}/*"),
        ],
      },
      {
        actions: [
          "execute-api:*",
        ],
        resources: [
          $concat(
            "arn:aws:execute-api:",
            region,
            ":",
            aws.getCallerIdentityOutput({}).accountId,
            ":",
            api.nodes.api.id,
            "/*/*/*"
          ),
        ],
      },
    ],
  },
});

여기서 어떤 작업을 하는지 살펴보겠습니다.

  • CognitoUserPool 컴포넌트는 Cognito 사용자 풀을 생성합니다. usernames 속성을 사용하여 사용자가 이메일로 로그인하도록 설정했습니다.

  • addClient를 사용하여 사용자 풀에 대한 클라이언트를 생성합니다. 각 _“클라이언트”_마다 하나씩 생성해야 합니다. 현재는 프론트엔드만 있으므로 하나만 필요합니다. 나중에 모바일 앱을 추가하면 또 다른 클라이언트를 추가할 수 있습니다.

  • CognitoIdentityPool 컴포넌트는 Identity Pool을 생성합니다. attachPermissionsForAuthUsers 함수를 사용하여 인증된 사용자가 접근할 수 있는 리소스를 지정할 수 있습니다.

  • 사용자가 S3 버킷과 API에 접근할 수 있도록 설정했습니다. 이 두 리소스는 각각 api.tsstorage.ts에서 가져옵니다. 이에 대해 아래에서 자세히 살펴보겠습니다.

접근 보안 설정

인증된 사용자가 API에 접근할 수 있도록 IAM 정책을 생성합니다. IAM에 대해 더 알아보기.

{
  actions: [
    "execute-api:*",
  ],
  resources: [
    $concat(
      "arn:aws:execute-api:",
      region,
      ":",
      aws.getCallerIdentityOutput({}).accountId,
      ":",
      api.nodes.api.id,
      "/*/*/*"
    ),
  ],
},

이 코드는 다소 복잡해 보이지만, Amazon API Gateway는 엔드포인트를 정의하기 위해 특정 형식을 사용합니다. 여기서는 그 형식을 구성하고 있습니다.

또한 사용자가 S3 버킷에 업로드할 파일을 보호하기 위해 특정 IAM 정책을 생성합니다.

{
  actions: ["s3:*"],
  resources: [
    $concat(bucket.arn, "/private/${cognito-identity.amazonaws.com:sub}/*"),
  ],
},

이 정책이 어떻게 동작하는지 살펴봅시다.

위 정책에서 로그인한 사용자에게 S3 버킷의 ARN 내 private/${cognito-identity.amazonaws.com:sub}/ 경로에 대한 접근 권한을 부여합니다. 여기서 cognito-identity.amazonaws.com:sub는 인증된 사용자의 연동된 아이디(사용자 ID)입니다. 따라서 각 사용자는 버킷 내 자신의 폴더에만 접근할 수 있습니다. 이를 통해 동일한 S3 버킷 내에서 사용자 파일 업로드에 대한 접근을 분리할 수 있습니다.

또한, 연동된 아이디는 Identity Pool에서 할당된 UUID입니다. 이 ID는 User Pool에서 사용자에게 할당된 ID와 다릅니다. 이는 여러 인증 프로바이더를 사용할 수 있기 때문입니다. Identity Pool은 이러한 아이디를 연동하고 각 사용자에게 고유한 ID를 제공합니다.

설정 파일에 추가하기

sst.config.ts 파일에 다음 내용을 추가해 보겠습니다.

Change indicator sst.config.ts 파일에서 await import("./infra/api") 줄 아래에 다음 내용을 추가합니다.

const auth = await import("./infra/auth");

return {
  UserPool: auth.userPool.id,
  Region: aws.getRegionOutput().name,
  IdentityPool: auth.identityPool.id,
  UserPoolClient: auth.userPoolClient.id,
};

여기서는 새로운 설정을 불러오고, return을 통해 터미널에 새로운 인증 리소스에 대한 유용한 정보를 출력할 수 있습니다.

API에 인증 추가하기

API에 인증 기능을 활성화해야 합니다.

Change indicator infra/api.ts 파일에서 handler: { 블록 아래의 transform 옵션에 다음 속성을 추가하세요.

args: {
  auth: { iam: true }
},

최종적으로 다음과 같은 형태가 됩니다.

// API 생성
export const api = new sst.aws.ApiGatewayV2("Api", {
  transform: {
    route: {
      handler: {
        link: [table],
      },
      args: {
        auth: { iam: true }
      },
    }
  }
});

이 설정은 모든 라우트에서 AWS_IAM 인증을 사용하도록 API에 지시합니다.

변경 사항 배포하기

터미널로 이동하면 변경 사항이 배포되고 있는 것을 확인할 수 있습니다.

새로운 인증 리소스가 배포되고 있는 것을 볼 수 있습니다.

+  Complete
   Api: https://5bv7x0iuga.execute-api.us-east-1.amazonaws.com
   ---
   IdentityPool: us-east-1:9bd0357e-2ac1-418d-a609-bc5e7bc064e3
   Region: us-east-1
   UserPool: us-east-1_TYEz7XP7P
   UserPoolClient: 3fetogamdv9aqa0393adsd7viv

API를 테스트할 수 있도록 테스트 사용자를 생성해 보겠습니다.

테스트 사용자 생성

이메일과 비밀번호로 사용자를 등록하기 위해 AWS CLI를 사용합니다.

Change indicator 터미널에서 다음 명령어를 실행하세요.

$ aws cognito-idp sign-up \
  --region <COGNITO_REGION> \
  --client-id <USER_POOL_CLIENT_ID> \
  --username admin@example.com \
  --password Passw0rd!

COGNITO_REGIONUSER_POOL_CLIENT_ID를 위에서 확인한 RegionUserPoolClient 값으로 대체해야 합니다.

이제 이메일을 확인해야 합니다. 현재는 관리자 명령어를 통해 확인하겠습니다.

Change indicator 터미널에서 다음 명령어를 실행하세요.

$ aws cognito-idp admin-confirm-sign-up \
  --region <COGNITO_REGION> \
  --user-pool-id <USER_POOL_ID> \
  --username admin@example.com

COGNITO_REGIONUSER_POOL_ID를 위에서 확인한 RegionUserPool 값으로 대체해야 합니다.

이제 인증 인프라와 테스트 사용자가 생성되었으니, 이를 사용해 API를 보호하고 테스트해 보겠습니다.