💭

【Amplify】 Lambda認証を使って外部の認証システムを組み込んでAppsyncを制御する

2024/02/05に公開

はじめに

Appsyncに認証を設定する際、以下5つの方法が存在します。

  1. API_KEY 認証
  2. AWS_LAMBDA 認証
  3. AWS_IAM 認証
  4. OPENID_CONNECT 認証
  5. AMAZON_COGNITO_USER_POOLS 認証

この中でも、外部のシステムを用いて認証を行いたい場合や、DynamoDBに認証情報を持たせてそれを使って認証を行いたい場合など、柔軟な認証に対応するにはLambda認証が使えます。

今回はAmplifyで、Lambda認証を用いてAppsync実装を行なっていこうと思います。

AppsyncにLambda認証を適用する

Appsyncを追加もしくは更新する

  • amplify add apiを実施して、Graphqlを選択します。Graphqlを既に作成している場合はamplify update apiを実施してください。
  • 既存の Lambda を認証用 Lambda として選択することも可能です。
amplify add api  

? Select from one of the below mentioned services: GraphQL
? Here is the GraphQL API that we will create. Select a setting to edit or continue Authorization modes: API key (default, expira
tion time: 7 days from now)
// API Lambdaを選択
? Choose the default authorization type for the API Lambda
// カスタム認証を実施する Lambda を作成する
? Choose a Lambda authorization function Create a new Lambda function
? Do you want to edit the local lambda function now? Yes
Edit the file in your editor: /<project-name>/amplify/backend/function/<lambda-name>/src/index.js
? Press enter to continue 
Successfully added <lambda-name> function locally
// 一度認可された場合のキャッシュ時間の設定。ここで設定された時間の間は認証Lambdaは再度実行されない。デフォルトは300秒。
? How long should the authorization response be cached in seconds? 300
// Lambda認証以外の認証方法を使用する場合はここでYesを選択して追加する
? Configure additional auth types? No
? Here is the GraphQL API that we will create. Select a setting to edit or continue Continue
? Choose a schema template: Single object with fields (e.g., “Todo” with ID, name, description)

作成したLambdaに認証用ロジックを追加する

amplify/backend/function/<lambda-name>/src/index.jsに認証ロジックを追加していきます。

export const handler = async (event) => {
  const { authorizationToken } = event;
  
  // authorizationTokenを使って認証を行う
  // 外部の認証用API叩いてトークンの有効性を確認する
  const isAuthorized = fetch('https://hogehoge.com/me', { headers: { Authorization: `Bearer ${authorizationToken}` } });
  
  // 認証に成功した場合は isAuthorized をtrueで返す
  if (isAuthorized) {
      return {
        isAuthorized: true,
	// amplify cli で設定したキャッシュ時間をここで上書きできる
        ttlOverride: 300,
      };
   // 認証に失敗した場合は isAuthorized をfalseで返す
   } else {
      return {
        isAuthorized: false,
        ttlOverride: 0,
      };
   }
}; 

ここで押さえておくべきポイントは以下です。

  • event に格納されている authorizationToken は Authorizationヘッダーの値です。
  • 関数の戻り値のisAuthorizedのブール値で認証されるかどうかを判定します。
  • amplify cliで設定したキャッシュ時間を関数の戻り値のttlOverrideの値で上書きが可能です。

amplify cliで設定したキャッシュ時間を忘れてしまった場合は、amplify/backend/backend-config.jsonにて確認できます。


  "api": {
    "api-name": {
      "service": "AppSync",
      "providerPlugin": "awscloudformation",
      "dependsOn": [],
      "output": {
        "authConfig": {
          "defaultAuthentication": {
            "authenticationType": "AWS_LAMBDA",
            "lambdaAuthorizerConfig": {
              "lambdaFunction": "lambda-name",
	      // 設定したキャッシュ時間はここを参照
              "ttlSeconds": "300"
            }
          },
          "additionalAuthenticationProviders": []
        }
      }
    }
  }

スキーマを修正する

作成した認証用LambdaをAppsyncに割り当てていきます。amplify/backend/api/<api-name>/schema.graphqlを以下のように修正してきます。

  • Lambda認証を割り当てたいモデルに対して@authディレクティブを以下のように追加する
type Todo @model @auth(rules: [{ allow: custom }]) {
  id: ID!
  name: String!
  description: String
}

これでバックエンドの準備は完了したので、amplify pushを実行してデプロイします。

Amplify Librariesを使ってGraphqlを実行してみる

Amplify LibrariesはAmplify CLIとセットで使われることが多いと思いますので、Amplify Librariesを使ったフロントエンド実装もご紹介していこうと思います。

以下がJavascriptのサンプルコードです。

// 認証用Lambdaに渡すトークンを用意
const getAuthToken = () => 'myAuthToken';
const lambdaAuthToken = getAuthToken();

const getTodo = await API.graphql({
  query: queries.getTodo,
  variables: {input: todoDetails},
  // authModeに AWS_LAMBDA を設定
  authMode: 'AWS_LAMBDA',
  // トークンはここに設定するか直接Authorizationヘッダーに格納する
  authToken: lambdaAuthToken
});

実際に動作確認してみようと思います。

正しいトークンを渡すとステータスコード200と共に、以下のようにレスポンスが返ってきました。

無事認証に成功して、データの取得ができました。

では次に、間違ったトークンを渡した場合はどうなるでしょうか。
ステータスコード401と共に、以下のようにレスポンスが返ってきました。

意図した通り、トークンが一致しない場合はクエリーが実行できないようになっています。

まとめ

今回はAmplifyで、Lambda認証を用いてAppsyncの制御を行う方法についてご紹介させていただきました。ただ、Lambda認証はカスタマイズ性に富んでいて柔軟な反面、余計なバグを生むリスクも生じてくるので、できるだけ認証部分はCognitoを使うことをお勧めします。

PURPM MEDIA LAB Tech blog

Discussion