Auth0 M2M 토큰을 캐싱하여 $$$ 절약

5432 단어 nodeawsdynamodbauth0
Auth0은 모든 응용 프로그램 인증 요구 사항을 처리하는 통합 서비스입니다. 그러나 이전에 작업한 적이 있다면 그것이 몰락한다는 것을 알게 될 것입니다.

M2M(Machine-to-Machine) 토큰 중 하나입니다. 서비스 간 인증에 사용됩니다.

그러나 제한은 서버리스 인프라에 대해 제한적입니다. 무료 플랜에서는 한 달에 1000만 받습니다. 그리고 유료 플랜에서도 주어진 달에 필요할 수 있는 토큰 수를 얻는 데 비용이 많이 듭니다.

해결책은 Machine-to-Machine 토큰을 캐시하여 만료될 때까지 새 토큰을 요청할 필요가 없도록 하는 것입니다.

전통적인 인프라에서는 이것이 사소한 일입니다. 토큰을 전역적으로 어딘가에 저장하고 완료합니다.

서버리스 아키텍처는 인스턴스 간에 지속성이 없기 때문에 까다롭습니다.

다음은 AWS Lambda 마이크로서비스용 Auth0 토큰 캐싱을 처리하는 방법입니다. 그러나 다른 클라우드 공급자에게도 동일한 원칙이 적용됩니다.

DynamoDB 테이블 생성



(또는 다른 클라우드 공급자의 동등한 서버리스 DB 테이블)

테이블에 고유한 이름을 설정하고 파티션 키를 문자열로 token로 설정합니다.



테이블 이름을 환경 변수로 추가CACHE_TOKEN_DB

토큰 검색 및 저장



먼저 새로운 M2M을 저장하는 방법을 추가하겠습니다.

// ===
// cacheToken.ts
// ===
import AWS from 'aws-sdk';

const storeNewToken = async (token: string) => {
  const docClient = new AWS.DynamoDB.DocumentClient();
  const response = await docClient.put({ TableName: `${process.env.TOKEN_CACHE_DB}`, Item: { token } }).promise();
  return response;
};


코드는 충분히 간단하고 자명합니다.

이제 새로운 M2M 토큰을 검색하기 위해 Lambda 핸들러에서 사용할 수 있는 메서드를 추가해 보겠습니다.

이 방법에는 두 가지 경로가 있습니다.
  • DynamoDB에 만료되지 않은 기존 토큰이 있으므로 이를 사용합니다.
  • 토큰이 없거나 만료된 토큰만 있으므로 새 토큰을 생성하여 DynamoDB에 저장하고 사용합니다.

  • 우리는 이 시스템을 한 번에 하나의 토큰만 저장하도록 설계할 것입니다. 즉, 오래된 토큰에 대해 걱정할 필요가 없고 초기화할 때마다 필터링할 필요가 없습니다.

    그래서 우리의 방법을 작성합시다!

    // ===
    // cacheToken.ts
    // ===
    import request from 'request-promise';
    
    export const getAuthToken = async (): Promise<string> => {
      const token = await getExistingToken();
      if (token !== '' && hasTokenExpired(token) === false) {
        return token;
      }
    
      const params = {
        method: 'POST',
        url: `https://${process.env.AUTH0_NAME}.auth0.com/oauth/token`,
        headers: { 'content-type': 'application/json' },
        body: `{"client_id":"${process.env.AUTH0_CLIENT_ID}","client_secret":"${process.env.AUTH0_CLIENT_SECRET}","audience":"${process.env.AUTH0_AUDIENCE}","grant_type":"client_credentials"}`,
      };
    
      const result = JSON.parse(await request(params));
      if (!result["access_token"]) { throw new Error("No Access Token returned"); }
    
      await deletePreviousTokens(token);
      await storeNewToken(result['access_token']);
    
      return result["access_token"];
    };
    


    이것을 조금 분해하자
  • 먼저 DynamoDB에서 기존 토큰을 가져옵니다. 토큰 또는 빈 문자열을 반환합니다.
  • 토큰을 반환하면 만료되지 않았는지 확인한 다음 해당 토큰을 반환합니다.
  • 토큰이 만료되었거나 토큰이 없는 경우 Auth0에서 토큰을 생성합니다.
  • 그런 다음 DynamoDB에서 이전 토큰을 삭제하고 새 토큰을 저장합니다.

  • 잠재적으로 이 흐름(및 DynamoDB가 비잠금이라는 사실)으로 인해 서비스의 여러 인스턴스가 동시에 토큰을 저장한다는 것을 의미할 수 있습니다. 그러나 이것은 처음에 캐싱을 통해 얼마나 절약할 수 있는지에 비하면 미미할 것입니다.

    이제 토큰 저장 및 유효성 검사와 상호 작용하는 데 도움이 되는 getAuthToken 함수에서 참조한 메서드를 생성해 보겠습니다.

    // ===
    // cacheToken.ts
    // ===
    import jwt_decode from 'jwt-decode';
    
    const deletePreviousTokens = async (token: string) => {
      const docClient = new AWS.DynamoDB.DocumentClient();
      const tokenRecords = await getAllTokens();
    
      // Clear down the table
      if (tokenRecords.Items) {
        tokenRecords.Items.forEach(async (row) => {
          const token = row.token;
          await docClient.delete({ TableName: `${process.env.TOKEN_CACHE_DB}`, Key: { "token": token } }).promise();
        });
      }
    };
    
    const hasTokenExpired = (token: string) => {
      const decoded = jwt_decode(token) as { exp: number; iat: number; };
      if (decoded) {
        return decoded.exp < (new Date().getTime() / 1000);
      }
    
      return false;
    };
    
    const getAllTokens = async () => {
      const docClient = new AWS.DynamoDB.DocumentClient();
      const response = await docClient.scan({
        TableName: `${process.env.TOKEN_CACHE_DB}`
      }).promise();
    
      return response;
    };
    
    const getExistingToken = async () => {
      const response = await getAllTokens();
    
      if (response.Items && response.Items.length > 0) {
        return response.Items[0]['token'];
      }
    
      return '';
    };
    


    다시, 이것을 분해합시다.
  • deletePreviousTokens에서 모든 기존 토큰을 가져와서 하나씩 삭제합니다. 이것은 다른 인스턴스가 우리가 삭제하고 싶지 않은 새 토큰을 작성한 동시성 문제를 피하기 위한 것입니다.
  • hasTokenExpired에서는 기본 JWT 유효성 검사를 수행하여 만료되지 않았는지 확인합니다. 1ms만 남았지만 지금까지 효과가 있었다면 토큰을 사용하지 않음으로써 개선할 수 있습니다.
  • getExistingToken에서는 테이블의 모든 행을 가져오고 첫 번째 토큰을 반환하거나 아무것도 없으면 빈 문자열을 반환합니다.

  • 핸들러에서의 사용법



    이제 남은 작업은 이를 Lambda 함수 핸들러 메서드에 추가하는 것입니다.

    export const handler = async (event: any, context: any) => {
        const token = await getAuthToken();
    
      // Do something with the token
      await sendResultsToService(token, event.Results);
    }
    


    이 내용이 흥미롭고 Auth0 청구서에 약간의 비용을 절약하셨기를 바랍니다!

    좋은 웹페이지 즐겨찾기