【Amplify】 Lambda認証を使って外部の認証システムを組み込んでAppsyncを制御する
はじめに
Appsyncに認証を設定する際、以下5つの方法が存在します。
- API_KEY 認証
- AWS_LAMBDA 認証
- AWS_IAM 認証
- OPENID_CONNECT 認証
- 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を使うことをお勧めします。
Discussion