본문 바로가기

ChatGPT/AWS Serverless

[AWS][Cognito] Express 그리고 Serverless Framework 연동하기 - 2

반응형

https://tobelinuxer.tistory.com/60

 

AWS Cognito와 Express 그리고 Serverless Framework 연동하기 - 1

AWS Cognito와 서버리스 아키텍처를 이용하여 안전하고 효율적인 로그인 시스템을 구축하는 방법에 대한 글을 작성하겠습니다. 이 글에서는 TypeScript와 Serverless Framework를 사용하여 AWS Cognito와 연동

tobelinuxer.tistory.com

이전 포스팅에 이어, 이 페이지에서는 3.프로젝트 구조, 4. AWS Cognito를 사용하여 로그인 시스템 구축하는 방법 5. Custom Authorizer를 이용한 토큰 검증 구현을 다루도록 하겠습니다.


1. 서론

  • AWS Cognito와 서버리스 아키텍처를 이용한 로그인 시스템 구축의 필요성

2. 사용된 기술 및 라이브러리 소개

  • TypeScript, Serverless Framework, AWS Lambda, API Gateway, Cognito
  • 사용된 라이브러리: Express, Axios, jsonwebtoken, jwks-rsa 등

3. 프로젝트 구조 및 설정

  • 프로젝트 디렉토리 구조 설명
  • serverless.yml 파일 설정
  • TypeScript 설정

4. AWS Cognito를 사용하여 로그인 시스템을 구축하는 방법

  • Cognito 사용자 풀 생성 및 설정
  • libs/cognito.ts: Cognito 사용자 정보 조회 및 토큰 관련 함수 구현
  • 로그인 및 토큰 발급 과정 설명

5. Custom Authorizer를 이용한 토큰 검증 구현

  • libs/authorization.ts: 토큰 검증 및 권한 관리 함수 구현
  • auth 디렉토리의 각 Authorizer 핸들러 설명
  • 토큰 검증 과정 및 Authorizer 작동 원리

6. AWS에 배포 및 테스트

  • Serverless Framework를 이용한 배포 방법
  • 포된 API 엔드포인트를 이용한 로그인 시스템 테스트

7. 결론

  • 이번 포스트를 통해 구현한 인증 시스템의 요약
  • 보안, 확장성, 관리 등의 관점에서 추가로 고려해 볼 사항
  •  

3. 프로젝트 구조 및 설정


3.1. 프로젝트 디렉토리 구조

 

우선, 프로젝트 디렉토리 구조는 다음과 같습니다:

https://github.com/gboysking/serverless-framework-basic/tree/master/my-service

 

GitHub - gboysking/serverless-framework-basic

Contribute to gboysking/serverless-framework-basic development by creating an account on GitHub.

github.com

.
├── src
│   ├── auth
│   │   ├── cookieAuthorizer.ts
│   │   ├── cognitoAuthorizer.ts
│   │   ├── requestAuthorizer.ts
│   │   └── tokenAuthorizer.ts
│   ├── handler.ts
│   └── libs
│       ├── authorization.ts
│       ├── cognito.ts
│       ├── express.ts
│       └── interface.ts
└── serverless.yml
  • src: 프로젝트의 소스 코드를 담은 디렉토리입니다.
  • auth: Custom Authorizer들을 구현한 파일들이 위치하는 디렉토리입니다.
  • handler.ts: 각 Lambda 함수에 대한 핸들러를 정의하는 파일입니다.
  • libs: 프로젝트에서 사용되는 라이브러리 및 유틸리티 함수들을 모아놓은 디렉토리입니다.
  • serverless.yml: Serverless Framework의 설정 파일로, AWS Lambda 및 API Gateway 설정을 포함합니다.

3.2. serverless.yml 파일 설정

 

serverless.yml 파일은 Serverless Framework의 설정 파일로, 프로젝트의 구성요소와 배포 관련 설정이 포함되어 있습니다.

serverless.yml 파일에는 다음과 같은 내용이 포함되어 있습니다:

 

  • 서비스 이름, 플러그인, 사용자 정의 설정, 공급자 설정 (AWS, 런타임, 메모리 크기, 타임아웃, 스테이지, 지역 등)
    함수 정의: 각각의 Lambda 함수에 대해 핸들러, 이벤트 및 환경 변수를 설정합니다. 여기서 함수는 tokenAuthorizer, requestAuthorizer, cognitoAuthorizer, cookieAuthorizer 등의 Custom Authorizer와 관련된 함수들, 그리고 app, appToken, appRequest, appUserToken, appUserCookie, authorize와 같은 API Gateway와 연결된 함수들이 포함됩니다.
  • 각 함수는 이벤트에 대한 정보 (예: 경로, 메서드, CORS, 작성자) 및 환경 변수를 설정할 수 있습니다.
service: my-service

plugins:
  - serverless-esbuild
  - serverless-offline

custom:
  serverless-offline:
    httpPort: 3000

provider:
  name: aws
  runtime: nodejs16.x
  lambdaHashingVersion: 20201221
  memorySize: 128
  timeout: 5
  stage: dev
  region: ap-northeast-2

functions:
  tokenAuthorizer:
    handler: src/auth/tokenAuthorizer.handler
  requestAuthorizer:
...생략... 
  app:
    handler: src/handler.app_handler
    events:
      - http:
...생략...
  appToken:
    handler: src/handler.app_handler
    events:
      - http:
...생략...
          authorizer:
            type: TOKEN
            name: tokenAuthorizer
            identitySource: method.request.header.Authorization                
  appRequest:
    handler: src/handler.app_handler
    events:
      - http:
...생략...
          authorizer:
            type: REQUEST
            name: requestAuthorizer
            identitySource:
              - method.request.header.CustomHeader
  appUserToken:
    handler: src/handler.app_handler
    environment:
      COGNITO_USER_POOL_DOMAIN_NAME: ${env:COGNITO_USER_POOL_DOMAIN_NAME}      
    events:
      - http:
...생략...
          authorizer:
            type: TOKEN
            name: cognitoAuthorizer
            resultTtlInSeconds: 0
            identityValidationExpression: ".*"                
  appUserCookie:
    handler: src/handler.app_handler
    environment:
      COGNITO_USER_POOL_DOMAIN_NAME: ${env:COGNITO_USER_POOL_DOMAIN_NAME}         
    events:
      - http:
...생략...
          authorizer:
            type: REQUEST
            name: cookieAuthorizer
            identitySource:
              - method.request.header.Cookie    
            resultTtlInSeconds: 0
            identityValidationExpression: ".*"          
  authorize:
    handler: src/handler.authorize_handler
    environment:
      AUTH_AUTHORIZATION_URI: ${env:AUTH_AUTHORIZATION_URI}
      AUTH_TOKEN_URI: ${env:AUTH_TOKEN_URI}
      AUTH_CLIENT_ID: ${env:AUTH_CLIENT_ID}
      AUTH_CLIENT_SECRET: ${env:AUTH_CLIENT_SECRET}
    events:
      - http:
          path: /authorize/{proxy+}
          method: ANY
          cors: true

 

이러한 설정을 통해 Serverless Framework를 사용하여 TypeScript 프로젝트를 AWS Lambda와 API Gateway와 함께 배포할 수 있습니다. ( 풀 코드를 원하시면 상단 github에 방문하셔서 확인바랍니다.)


이 프로젝트 구조와 설정을 사용하면 AWS Cognito와 연동하여 로그인 시스템을 구축하는 데 필요한 구성 요소를 쉽게 관리할 수 있습니다.

 

각각의 Custom Authorizer는 다양한 인증 방식에 대응할 수 있도록 구현되어 있으며, 이를 통해 토큰 검증 및 사용자 인증을 처리할 수 있습니다.

 

다음 단계에서는 각 함수에 대한 코드를 자세히 설명하고, 이들이 어떻게 서로 연동되는지 살펴보겠습니다.

 

이를 통해 프로젝트 전체의 흐름을 이해할 수 있으며, 이를 바탕으로 TypeScript와 Serverless Framework를 활용하여 AWS Cognito와 연동하는 방법, 로그인 시스템 구축, Custom Authorizer를 이용한 토큰 검증 구현, 그리고 배포 과정에 대한 내용을 설명할 것입니다.

 

이를 통해 여러분의 프로젝트에 필요한 인증 및 인가 기능을 구현하는 데 도움이 될 것입니다.


 

4. AWS Cognito를 사용하여 로그인 시스템을 구축하는 방법

 

다음은 구축할 로그인 시스템의 주요 구성 요소입니다.

  • Cognito User Pool 생성
  • Cognito Identity Pool 생성
  • Cognito를 사용한 로그인 및 토큰 발급
  • Custom Authorizer를 사용한 토큰 검증

Cognito User Pool 생성

 

먼저, AWS Management Console에 로그인한 후, Cognito 서비스로 이동합니다.


Cognito User Pool은 사용자 정보를 저장하고 관리하는 곳입니다.


여기서 새로운 User Pool을 생성하고, 필요한 속성과 정책을 설정합니다.

 

 

먼저, 테스트할 수 있게 사용자를 하나 등록했습니다.


Cognito Identity Pool 생성

 

Identity Pool은 사용자를 인증하고 AWS 리소스에 대한 접근 권한을 부여하는 데 사용됩니다.


Cognito User Pool을 사용하여 생성한 Identity Pool을 사용하면 사용자 인증 정보를 기반으로 한 리소스 접근 권한을 설정할 수 있습니다.

 

현재 예제에서는 생략합니다.


Cognito를 사용한 로그인 및 토큰 발급

 

로그인의 시작 지점은 /authorize/login 엔드포인트 부터 시작합니다.

 

해당 엔드포인트에서는 로그인 인증을 위해 OAuth 2.0 인증 코드 플로우를 사용합니다.

 

app.get('/login', (req: Request, res: Response, next: NextFunction) => {
  const protocol = req.protocol;
  const hostname = req.get('host');
  const callbackPath = '/authorize/callback';

  let callbackUrl = `${protocol}://${hostname}${callbackPath}`;

  if (process.env.IS_OFFLINE) {
    callbackUrl = `http://${hostname}/dev${callbackPath}`;
  }

  if (req.cookies && req.cookies.credentials) {

    let accessToken = req.cookies.credentials.access_token;
    let refreshToken = req.cookies.credentials.refresh_token;    

    isTokenExpired(accessToken)
      .then(async (result) => {

        if (result) {
          const newCredentials = await getAccessTokenWithRefreshToken(refreshToken, process.env.AUTH_CLIENT_ID, process.env.AUTH_CLIENT_SECRET, process.env.AUTH_TOKEN_URI);
          console.log("credentials update.");          
          res.cookie('credentials', JSON.stringify(newCredentials), { httpOnly: true, secure: true });
          res.redirect('/dev/app-user-cookie/hello');

        } else {
          res.redirect('/dev/app-user-cookie/hello');
        }
      })
      .catch((_err)=> {
        console.log("cookie credentials error. redirect to login page.");
        const url = getAuthorizationUrl({ ClientId: process.env.AUTH_CLIENT_ID, RedirectUri: callbackUrl, ResponseType: "code", Scope: "email openid", State: "" })

        res.redirect(url);
      })
  } else {
    const url = getAuthorizationUrl({ ClientId: process.env.AUTH_CLIENT_ID, RedirectUri: callbackUrl, ResponseType: "code", Scope: "email openid", State: "" })

    res.redirect(url);
  }
});

 

먼저 callbackUrl을 정의하는데, req.protocol과 req.get('host')를 사용하여 현재 프로토콜과 호스트 정보를 가져와 '/authorize/callback' 경로와 합쳐서 정의합니다.

 

만약 process.env.IS_OFFLINE 환경 변수가 설정된 경우에는 호스트 정보에 '/dev'를 추가하여 로컬에서 테스트하기 쉽게 합니다.



그 다음에는 쿠키에 저장된 사용자 인증 정보가 있는지 확인합니다. 

 

쿠키에 사용자 인증 정보가 있다면, 해당 쿠키에서 access_token을 추출하고 isTokenExpired() 함수를 사용하여 access_token이 만료되었는지 확인합니다. 

 

access_token이 만료되었다면 getAccessTokenWithRefreshToken() 함수를 사용하여 refresh_token으로 새로운 access_token을 발급받아 쿠키에 저장합니다. 

 

access_token이 유효한 경우에는 바로 /dev/app-user-cookie/hello 엔드포인트로 리다이렉트합니다.

쿠키에 사용자 인증 정보가 없다면, getAuthorizationUrl() 함수를 사용하여 OAuth 2.0 인증 코드 플로우를 진행하는 URL을 생성하고, 해당 URL로 리다이렉트합니다.

 

로그인 처리를 위해, libs/authorization.ts 파일에 getAccessToken 함수를 사용합니다.

 

이 앤드 포인트 /authorize/callback은 인증 코드를 받아, Cognito User Pool에서 액세스 토큰을 발급받는 역할을 합니다.

 

app.get('/callback', (req: Request, res: Response, next: NextFunction) => {
  const { code } = req.query;

  const protocol = req.protocol;
  const hostname = req.get('host');
  const callbackPath = '/authorize/callback';

  let callbackUrl = `${protocol}://${hostname}${callbackPath}`;

  if (process.env.IS_OFFLINE) {
    callbackUrl = `http://${hostname}/dev${callbackPath}`;
  }

  getAccessToken(code as string, callbackUrl, process.env.AUTH_CLIENT_ID, process.env.AUTH_CLIENT_SECRET, process.env.AUTH_TOKEN_URI)
    .then((result) => {
      res.cookie('credentials', JSON.stringify(result), { httpOnly: true, secure: true }); // 쿠키 저장
      res.redirect('/dev/app-user-cookie/profile');
      // res.json(result.data);
    })
    .catch((reason: any) => {
      next(reason);
    })
    ;
}
);

 

이 함수에서는 req.query로부터 인증 코드를 받아와 getAccessToken 함수를 호출하여 엑세스 토큰을 발급받습니다. 

 

그리고 발급받은 엑세스 토큰을 쿠키에 저장하고 /dev/app-user-cookie/profile 경로로 리다이렉트합니다.

 

export let getAccessToken = (code: string, redirectUri: string, clientId: string, clientSecret: string, tokenUri: string): Promise<Credentials> => {
  return new Promise((resolve, reject) => {
    const body = {
      grant_type: "authorization_code",
      code: code,
      client_id: clientId,
      redirect_uri: redirectUri,
    };

    const credentials = `${clientId}:${clientSecret}`;
    const base64EncodedCredentials = Buffer.from(credentials).toString('base64');

    axios.post<Credentials>(tokenUri, body, {
      headers: {
        'Content-Type': "application/x-www-form-urlencoded",
        Authorization: `Basic ${base64EncodedCredentials}`
      }
    })
      .then((response) => {
        resolve(response.data);
      })
      .catch((error) => {
        reject(error);
      });
  });
}

 

이 함수를 이용해  Cognito로부터 액세스 토큰을 받아옵니다.

 

이 때, authorization header에 클라이언트 ID와 secret을 base64로 인코딩하여 전송합니다.



그리고 Promise를 이용해 비동기적으로 access token을 발급받은 후 결과 값을 resolve하거나, error가 발생하면 reject합니다


Custom Authorizer를 사용한 토큰 검증

 

로그인 후 발급받은 액세스 토큰을 사용해 API 요청에 인증 정보를 포함시킬 수 있습니다.

 

이때 Custom Authorizer를 사용하여 토큰을 검증하고, API 요청을 처리할 수 있습니다.

 

예를 들어, auth/cognitoAuthorizer.ts 파일에 있는 handler 함수는 액세스 토큰을 검증하는 역할을 합니다.

 

export const handler = async (event: APIGatewayTokenAuthorizerEvent, _context: Context): Promise<CustomAuthorizerResult> => {
    const token = event.authorizationToken;
    // ... 생략 ...
}

 

여기까지 AWS Cognito를 활용한 로그인 시스템 구축 방법에 대해 알아보았습니다.

 

이제 사용자 인증을 처리하고, 토큰 검증을 통해 API 요청에 인증 정보를 포함시키는 방법을 살펴보겠습니다.


4.1. API 요청에 인증 정보 포함하기

 

my-service는 authorize/login을 통해 로그인을 진행하면 cookie에 그 정보가 저장됩니다. 

 

이 경우, /app-user-cookie 엔드포인트를 사용하여 해당 쿠키를 인증하는 방식으로 사용자 정보에 접근할 수 있습니다.



그러나 OAuth2.0 인증 플로우를 Cognito로 직접 진행하였다면, AccessToken을 얻게 됩니다. 

 

이 경우, Authorization Header에 해당 AccessToken을 포함시키고 /app-user-token 엔드포인트를 이용하여 사용자 정보에 접근할 수 있습니다.



따라서, 사용자 정보를 가져오기 위해서는 다음과 같은 방법을 사용할 수 있습니다.



1. Cognito로부터 직접 AccessToken을 받아 /app-user-token 엔드포인트를 이용하여 사용자 정보에 접근하는 방법



2. my-service에서 제공하는 로그인 API를 사용하여 로그인을 하면, 해당 정보가 쿠키에 저장되며, /app-user-cookie 엔드포인트를 이용하여 사용자 정보에 접근하는 방법



위 두 가지 방법 중 적합한 방법을 선택하여 사용하면 됩니다.

 

1번 같은 경우 예를 들어, JavaScript의 fetch를 사용하여 요청을 보낼 때 다음과 같이 작성할 수 있습니다.

 

로그인 후 발급받은 액세스 토큰을 사용하여 API 요청에 인증 정보를 포함시키려면, Authorization 헤더에 토큰을 추가합니다. 

 

fetch('https://your-api-endpoint/api', {
  headers: {
    'Authorization': `Bearer ${access_token}`,
  },
});

4.2. Custom Authorizer로 토큰 검증하기

 

서버 측에서는 Custom Authorizer를 사용하여 토큰을 검증합니다. 

 

각 Authorizer 파일(tokenAuthorizer.ts, requestAuthorizer.ts, cognitoAuthorizer.ts, cookieAuthorizer.ts)에는 토큰 검증에 관한 로직이 포함되어 있습니다.



이러한 Custom Authorizer는 AWS Lambda를 사용하여 구현되며, 각 함수에서 토큰을 검증한 후에 적절한 정책을 반환합니다.



예를 들어, auth/cognitoAuthorizer.ts의 handler 함수는 다음과 같이 작성되어 있습니다.

 

import { APIGatewayTokenAuthorizerEvent, CustomAuthorizerResult, Context } from "aws-lambda";
import { generatePolicy, verifyToken } from "libs/authorization";

export const handler = async (event: APIGatewayTokenAuthorizerEvent, _context: Context): Promise<CustomAuthorizerResult> => {
    const token = event.authorizationToken;

    try {
        const tokenPart = token.split(" ");

        if (tokenPart[0] !== "Bearer") {
            throw new Error("This token is not Bearer type.");
        }

        const decoded = await verifyToken(tokenPart[1]);
        const policy = generatePolicy(decoded.sub, "Allow", event.methodArn);
        return policy;
    } catch (error) {
        console.error("Token verification failed:", error.message);
        throw new Error("Unauthorized");
    }
};

 

이 함수는 token이 Bearer type인지 확인하고, verifyToken 함수를 사용하여 token을 검증합니다. 

 

token이 유효하지 않으면 Unauthorized error를 반환합니다.



검증이 성공하면, 해당 token에 대한 권한이 있는지 확인하기 위해 generatePolicy 함수를 사용하여 IAM policy를 생성합니다.

 

이를 기반으로 API Gateway는 해당 사용자에 대한 권한을 부여하거나 거부합니다.


4.3. 토큰 검증 후 사용자 정보 처리

 

토큰이 검증되면, 사용자 정보를 처리하는 로직을 추가할 수 있습니다. 

 

사용자 정보는 Custom Authorizer를 통해 검증된 토큰에서 추출할 수 있습니다. 

 

이를 이용해 사용자별로 다른 데이터를 반환하거나, 사용자의 권한에 따라 접근을 제한할 수 있습니다.



이렇게 AWS Cognito를 활용한 로그인 시스템을 구축하고, 사용자 인증 및 토큰 검증을 처리하는 방법을 살펴보았습니다. 

 

이 방법을 사용하면 보안성이 높은 인증 및 권한 관리 시스템을 구축할 수 있습니다.


 

5. Custom Authorizer를 이용한 토큰 검증 구현

 

AWS Cognito와 연동하여 로그인 시스템을 구축한 후, 이제 사용자의 API 요청을 검증하는 방법을 살펴보겠습니다.

 

이를 위해 Custom Authorizer를 사용하여 토큰 검증을 구현하고, 사용자에게 적절한 접근 권한을 부여하는 방법을 소개합니다.


5.1. Custom Authorizer란?

 

Custom Authorizer는 AWS API Gateway에서 사용자의 API 요청을 검증하는 데 사용되는 Lambda 함수입니다.

 

이 함수는 요청 헤더, 쿠키, 쿼리 문자열 파라미터 등과 같은 입력 데이터를 기반으로 요청이 허용되거나 거부되는지 결정합니다.

 

Custom Authorizer를 사용하면, 서버 측에서 사용자의 토큰을 검증하고 적절한 정책을 반환할 수 있습니다.


5.2. 토큰 검증 구현

 

이 프로젝트에서는 4가지 종류의 Custom Authorizer를 구현하였습니다:

 

tokenAuthorizer.ts, requestAuthorizer.ts, cognitoAuthorizer.ts, cookieAuthorizer.ts. 각 Authorizer는 다른 방식으로 토큰을 검증하며, 각각의 상황에 맞게 사용할 수 있습니다.

 

예를 들어, auth/cookieAuthorizer.ts에서 토큰 검증을 수행하는 handler 함수를 살펴보겠습니다.

 

import { CustomAuthorizerResult, Context, APIGatewayRequestAuthorizerEvent } from "aws-lambda";
import { generatePolicy, verifyToken } from "libs/authorization";

export const handler = async (event: APIGatewayRequestAuthorizerEvent, _context: Context): Promise<CustomAuthorizerResult> => {
    const headers = event.headers;
    const cookies = headers ? headers.Cookie : null;

    if (cookies) {

        const parsedCookies = cookies.split(';').reduce((acc, cookie) => {
            const [key, value] = cookie.trim().split('=');
            acc[key] = decodeURIComponent(value);
            return acc;
        }, {});

        const credentials = JSON.parse(parsedCookies['credentials']);

        try {
            const decoded = await verifyToken(credentials.access_token);
            const policy = generatePolicy(decoded.sub, "Allow", event.methodArn, decoded);
            return policy;
        } catch (error) {
            console.error("Token verification failed:", error.message);
            throw new Error("Unauthorized");
        }
    }

    throw new Error("Unauthorized");
};

 

위 코드는 AWS Lambda의 Custom Authorizer 기능을 이용하여 Request Authorizer를 만드는 핸들러 함수입니다.



해당 함수는 APIGatewayRequestAuthorizerEvent 이벤트 객체와 Context 객체를 받아서 Promise<CustomAuthorizerResult> 타입의 결과를 반환합니다.



headers에서 Cookie 값을 가져와서 parsedCookies 객체에 파싱하고, 그 중 credentials 쿠키에 담긴 값에서 access_token을 추출합니다.



이후, 추출한 access_token을 이용하여 JWT 토큰을 검증하고, 검증에 성공하면 해당 사용자에 대한 IAM 정책을 생성하여 반환합니다.

 

 검증에 실패하면 Unauthorized 에러를 발생시킵니다.

 

반응형

 

const client = jwksClient({
  jwksUri: process.env.AUTH_JWKS_URI,
});

export let getKey = (header: JwtHeader, callback: (err: Error | null, signingKey?: string) => void): void => {
  client.getSigningKey(header.kid, (err, key) => {
    if (err) {
      callback(err);
      return;
    }
    const signingKey = key.getPublicKey();
    callback(null, signingKey);
  });
}

export let verifyToken = (token: string): Promise<any> => {
  return new Promise((resolve, reject) => {
    verify(token, (header, callback) => getKey(header, callback), { algorithms: ["RS256"] }, (err, decoded) => {
      if (err) {
        console.log(err);
        reject(err);
      } else {
        resolve(decoded);
      }
    });
  });
}

 

위 코드는 JSON Web Token(JWT)을 검증하기 위한 함수들이 구현된 파일입니다.

jwksClient 라이브러리를 이용하여 AWS Cognito User Pool에서 사용하는 JWKS(JSON Web Key Set) URI를 이용해 클라이언트의 공개 키를 가져와 JWT의 서명을 확인할 수 있는 getKey 함수와, 이전에 정의한 getKey 함수를 이용하여 JWT 검증을 수행하는 verifyToken 함수가 구현되어 있습니다.



이 중 verifyToken 함수는 verify 함수를 이용하여 JWT를 검증합니다. 

 

이때, verify 함수의 첫 번째 인자로 검증할 JWT를 전달하고, 두 번째 인자로 getKey 함수를 전달하여 JWT의 서명을 확인할 수 있는 공개 키를 가져오고, 옵션으로 알고리즘을 전달합니다. 

 

마지막으로, JWT 검증 결과를 콜백 함수로 전달합니다. 

 

이때, 검증이 성공하면 decoded에 해당 JWT의 payload가 담기고, 검증이 실패하면 err에 에러 메시지가 담깁니다.



따라서 verifyToken 함수를 사용하여 JWT 검증을 수행하고, 검증 결과에 따라 필요한 로직을 추가로 구현하면 됩니다.

 


5.3. Custom Authorizer 정책 생성 및 반환

 

토큰 검증 후, Custom Authorizer는 사용자에게 적절한 접근 권한을 부여하기 위해 정책을 반환합니다.

 

이 프로젝트에서는 libs/authorization.ts에 정책 생성 함수인 generatePolicy를 구현하였습니다.

 

이 함수는 사용자 ID, 허용 또는 거부 액션, API 메서드 ARN 및 선택적으로 디코딩된 토큰을 인수로 받아 정책 객체를 반환합니다.

 

이 정책 객체는 AWS에 의해 사용되어 사용자의 요청을 허용하거나 거부하는 데 사용됩니다.

export const generatePolicy = (
  principalId: string,
  effect: "Allow" | "Deny",
  resource: string,
  context?: any
) => {
  const policy: CustomAuthorizerResult = {
    principalId,
    policyDocument: {
      Version: "2012-10-17",
      Statement: [
        {
          Action: "execute-api:Invoke",
          Effect: effect,
          Resource: resource,
        },
      ],
    },
  };

  if (context) {
    policy.context = context;
  }

  return policy;
};

 

이 함수는 주어진 인수를 사용하여 CustomAuthorizerResult 형식의 정책 객체를 생성하고 반환합니다.

 

이 정책은 사용자가 허용되거나 거부된 API 호출을 표시하는 AWS에 전달되어 처리됩니다.


5.4. 서버리스 프레임워크를 사용한 Custom Authorizer 설정

 

서버리스 프레임워크를 사용하여 Custom Authorizer를 설정하려면, serverless.yml 파일에 함수 및 이벤트 설정을 추가해야 합니다.

 

각각의 Custom Authorizer에 대해 handler 경로를 지정하고, 요청 이벤트에 authorizer 설정을 추가합니다.

 

이렇게 하면 해당 요청에 대해 Custom Authorizer가 트리거됩니다.

 

예를 들어, appUserToken 함수의 경우 다음과 같이 설정됩니다.

appUserToken:
  handler: src/handler.app_handler
  environment:
    COGNITO_USER_POOL_DOMAIN_NAME: ${env:COGNITO_USER_POOL_DOMAIN_NAME}      
  events:
    - http:
        path: /app-user-token/{proxy+}
        method: ANY
        cors: true
        authorizer:
          type: TOKEN
          name: cognitoAuthorizer
          resultTtlInSeconds: 0
          identityValidationExpression: ".*"

 

이 설정을 사용하면, appUserToken 엔드포인트로 들어오는 모든 요청에 대해 cognitoAuthorizer가 트리거되어 토큰 검증이 수행됩니다.

 

이제 Custom Authorizer를 사용하여 토큰 검증 구현 방법을 알았습니다.

 

각 상황에 따라 적절한 Custom Authorizer를 선택하여 사용자의 요청에 대한 접근 권한을 관리할 수 있습니다.

 

자세한 코드를 보실려면,

https://github.com/gboysking/serverless-framework-basic/tree/master/my-service

 

GitHub - gboysking/serverless-framework-basic

Contribute to gboysking/serverless-framework-basic development by creating an account on GitHub.

github.com

 

방문해 주세요.

 

 

지금 보시는 글은 ChatGPT의 도움을 받아 작성되었습니다.

반응형