2020-12-30
- AWS AmplifyでAppSyncを使っている。
- GraphQLAPIを公開用にしたい。/非認証ユーザーでも使えるようにしたい。
- AppSyncの認証をAPI_KEYでしているが、手動でリフレッシュするのが嫌だ。
AppSyncを使ってGraphQL APIを公開する場合、何らかの方法で認証をする必要があります。
多くの場合、その手軽さからAPI_KEYを選択しがちです。
しかし、(Amplifyではなく)AppSyncのドキュメントを見たところ、
API キーは、最大 365 日間有効に設定可能で、該当日からさらに最大 365 日、既存の有効期限を延長できます。API キーは、パブリック API の公開が安全であるユースケース、または開発目的での使用が推奨されます。
とのこと。
くわえてこちらのIssueよると、一応API_KEYのリフレッシュ方法が案内されているものの、おそらく現在は手動でしかできないようです。
ほかの選択肢としてOPENID_CONNECT認証とAMAZON_COGNITO_USER_POOLS認証がありますが、この2つは何らかの形でログインが必要になります。
というわけで今回は認証にAWS_IAMを指定して、API_KEYじゃなくても、非認証ユーザーがQueryとmutationを飛ばせるようにしていきます。
1.認証をAWS_IAMにする
2.すると非認証ユーザーに対してCognito Identity PoolsのUnAuthenticated Roleが付与される。
3.そのUnAuthenticated Roleに対してqueryとmutationのポリシーをアタッチ。
なお、amplifyではschemaの@authディレクティブに応じたポリシーがアタッチされた
Authenticated Role及び、UnAuthenticated Roleが自動で生成されます。
- React 17.0.1
- TypeScript 4.0.3
- aws-amplify 3.3.13
- amplify-cli 4.40.1
$ amplify init
とフロントのセットアップは済ませたとして勧めていきます。
$ amplify add auth
でauthモジュールを追加します。途中
Allow unauthenticated logins? (Provides scoped down permissions that you can control via AWS IAM)という項目が出てくるので、必ずYesにしてください
? Do you want to use the default authentication and security configuration? (Use arrow keys):
細かい設定をしたいので
Manual configuration
? Select the authentication/authorization services that you want to use: (Use arrow keys)
User Sign-Up, Sign-In, connected with AWS IAM controls (Enables per-user Storage features for images or other content, Analytics, and more)
? Please provide a friendly name for your resource that will be used to label this category in the project
任意のラベル名
? Please enter a name for your identity pool.
任意のidentity pool名
? Allow unauthenticated logins? (Provides scoped down permissions that you can control via AWS IAM)
今回の肝。必ず
Yes
以降はお好みで大丈夫です。強いて言うなら3rd party authenticationやOAuthは特に無いのでNoでいいです。
$ amplify add api
でapiを作成します。
? Choose the default authorization type for the API
の項目はIAMを選択してください。ほかは任意で大丈夫です。
今回は未認証ユーザーがqueriesとmutationのcreateを使えるように指定します。
type Todo
@model(mutations: { create: "createTodo" }, subscriptions: null)
@auth(rules: [{ allow: public, provider: iam }]) {
id: ID!
name: String!
description: String
}
@auth部分がみそです。
ドキュメントのpublic authorizationの欄にサンプルがあります。それによると、
auth ディレクティブは、指定された認証モードのデフォルトのプロバイダを上書きすることができます。上記のサンプルでは、APIキーの代わりにCognito Identity Poolsの「UnAuthenticated Role」をパブリックアクセスに使用できるプロバイダとしてiamが指定されています。amplify add authと一緒に使用すると、CLIは「UnAuthenticated」ロール用にスコープダウンされたIAMポリシーを自動的に生成します。
つまり
allow: public:対象は非認証(public)ユーザーだよ
provider: iam:対象が非認証ユーザーなのでiamはUnAuthenticatedロールを付与
ということを表しています。
また、@modelでquery全てとmutationのcreateのみ生成するように指定しており、push時に生成されるUnAuthenticatedロールに自動的にそれらの実行権限が付与されます。
それでは
$ amplify push
してクラウドにpush + GraphQLコードの生成しましょう。
Do you want to generate code for your newly created GraphQL API
Yes
Choose the code generation language target
今回はフロントの都合でTypeScriptにします。
あとはデフォルトで大丈夫です。
リソースの反映が終わったらRoleの作成ができているか、確認しましょう。
Amplify Consoleへいき、バックエンドのページからCognitoへ飛びましょう。
そして右上の**IDプールの編集**へ飛び、
amplify-(プロジェクト名)-(環境名)-(数字?)-(unauth/auth)
のロールが設定されています。
このロールに対してポリシーが正しく設定されているかも確認しようと思います。
IAMのコンソールで先程のロールを検索にはっつけてアクセス権限を見ると、たしかに
- getTodo
- listTodo
- createTodo
のリソースが指定されていると思います。
ここまでで、ひとまずバックエンドの編集は完了です。
ここからフロントの編集をしてきます。
$ yarn add aws-amplify
を行っていることを前提とします。
//index.tsx
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import Amplify from "aws-amplify";
import config from "./aws-exports";
Amplify.configure(config);
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
//App.tsx
import React from "react";
import API from "@aws-amplify/api";
import * as mutations from "./graphql/mutations";
import * as queries from "./graphql/queries";
import { GRAPHQL_AUTH_MODE } from "@aws-amplify/api-graphql/lib/types";
//TypeSciprtの方は↑に注意
function App() {
const todoDetails = {
name: "test",
description: "test",
};
const generateTodo = async () => {
try {
await API.graphql({
query: mutations.createTodo,
variables: { input: todoDetails },
authMode: GRAPHQL_AUTH_MODE.AWS_IAM,//TypeScriptの場合のみ それ以外は"AWS_IAM"
});
} catch (e) {
console.error(e);
}
};
const getList = async () => {
try {
console.log(
await API.graphql({
query: queries.listTodos,
authMode: GRAPHQL_AUTH_MODE.AWS_IAM,//TypeScriptの場合のみ それ以外は"AWS_IAM"
})
);
} catch (e) {
console.error(e);
}
};
return (
<>
<button onClick={getList}>Query</button>
<button onClick={generateTodo}>Mutation</button>
</>
);
}
export default App;
認証モードがAWS_IAMのときは、graphqlOperation関数は使用しません。その代わり、
await API.graphql({
query: queries.listTodo,
authMode: GRAPHQL_AUTH_MODE.AWS_IAM, //TypeScriptの場合のみ それ以外は"AWS_IAM"
});
authModeパラメータを含んだオブジェクトを渡します。[ドキュメントはこちら]
ここで、プロジェクトにTypeScriptを使っている方は、GRAPHQL_AUTH_MODEという型をimportしてauthModeに`GRAPHQL_AUTH_MODE.AWS_IAM`を渡してください。
それ以外の方はドキュメント通り、文字列"AWS_IAM"で大丈夫です。
この現象に関しては現在Issueをとばして確認しています
注):原因は結構明らかでGRAPHQL_AUTH_MODEは文字列列挙型(enum)になっており、そこにstringを入れようとするのでエラーになります。
では実際に動作するか確認します。
mutationをとばすと....
DynamoDBに要素が追加されました。
続いて、qureyをとばすと....
dynamoDBのデータを取得できました。
今回は非常に単純なschemaとAppでしたがご容赦ください。
こちら
注): awsのリソースは消えているのでcloneしても実際のAPI操作はできません。