Amplify で既存Cognito と 既存APIGateway を使ってみる

2021.03.18

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

おはようございます、もきゅりんです。

皆さん Amplify してますか?

既存のバックエンドを使いたいけど、 SPA のフロントエンドだけは Amplify を使いたい、なんてことがあると思います。

フロントエンド・バックエンドをまるっと構築・管理ができる Amplify ですが、既存のリソースを使いたいときはどうするのかな、と思ったので対応してみました。

やりたいことは下図です。

Amplify で囲われている部分以外は既存リソースを利用します。

amplify_apii

Amplify 以外のバックエンドの準備

元は、Swagger と AWS SAM を使って Cognito オーソライザーを使って簡単な REST API HTTP 統合してみる になります。

ただ、API Gateway ではREST API リソースが非シンプルクロスオリジンの HTTP リクエストを受け取る場合、CORS サポートを有効にする必要があります。

非シンプルクロスオリジンとは、何を指しているかなどの詳細については RESTAPIリソースのCORSの有効化-AmazonAPI Gateway を参照して頂ければと思いますが、今回はカスタムヘッダーのリクエストがあるため、CORS サポートを有効化する必要があります。

手動で CORS 有効化対応して OpenAPI (Swagger) ファイルでエクスポートしたものが下記になります。

(自分のSwagger ファイルの書き方がダメだったりで、めちゃめちゃ CORS でハマりました。。)

この Swagger を使って API Gateway を更新する必要があります。

openapi: 3.0.1
info:
  title: sam-rest-api
  version: 1.0.0
servers:
- url: /dev
paths:
  /greeting:
    get:
      responses:
        200:
          description: 200 response
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: string
          content: {}
      x-amazon-apigateway-integration:
        httpMethod: GET
        uri: http://xxxxxxxxxxxxxxxxxx.ap-northeast-1.elb.amazonaws.com/greeting
        responses:
          default:
            statusCode: "200"
            responseParameters:
              method.response.header.Access-Control-Allow-Origin: '*'
        passthroughBehavior: when_no_match
        type: http
x-amazon-apigateway-gateway-responses:
  DEFAULT_5XX:
    responseParameters:
      gatewayresponse.header.Access-Control-Allow-Methods: 'GET,OPTIONS'
      gatewayresponse.header.Access-Control-Allow-Origin: '*'
      gatewayresponse.header.Access-Control-Allow-Headers: 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'
  DEFAULT_4XX:
    responseParameters:
      gatewayresponse.header.Access-Control-Allow-Methods: 'GET,OPTIONS'
      gatewayresponse.header.Access-Control-Allow-Origin: '*'
      gatewayresponse.header.Access-Control-Allow-Headers: 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'

Amplify でフロントエンドから既存Cognito と 既存API を利用する

本題です。

対応自体は非常に簡単でした。

Cognito との連携

まずは 既存 Cognitoとの連携ですが、 Amplify プロジェクトに既存の Cognito ユーザープールと ID プールを使用する を参考にして進めます。

npx create-react-app demo-app
cd demo-app
npm start
amplify init

? Enter a name for the project demoapp
? Enter a name for the environment dev
? Choose your default editor: Visual Studio Code
? Choose the type of app that you're building javascript
Please tell us about your project
? What javascript framework are you using react
? Source Directory Path:  src
? Distribution Directory Path: build
? Build Command:  npm run-script build
? Start Command: npm run-script start
? Select the authentication method you want to use: AWS profile

ちなみにですが、 profile が hogehoge.fuga となっていると、hogehoge しか読んでくれなくてconfigの読み込みが失敗します。

amplify import auth
Using service: Cognito, provided by: awscloudformation
✔ What type of auth resource do you want to import? · Cognito User Pool only
✔ Select the User Pool you want to import: · ap-northeast-1_USER_POOL_ID
✔ Only one Web app client found: 'APP_CLIENT_NANE' was automatically selected.
✔ Only one Native app client found: 'APP_CLIENT_NANE' was automatically selected.
⚠️ It is recommended to use different app client for web and native application.
✔ Federated identity providers are not configured, no OAuth configuration needed.

✅ Cognito User Pool 'USER_POOL_NAME' was successfully imported.

amplify push とすると、 aws-exports.js に自動で Congito の設定が追加されます。

ログインを実装するため、2つの Amplify ライブラリをインストールします。

aws-amplify ライブラリには、使用するさまざまな AWS のサービスとやり取りするためのすべてのクライアント側 API が含まれ、@ aws-amplify/ui-react ライブラリにはフレームワーク固有の UI コンポーネントが含まれています。これらのライブラリをプロジェクトのルートにインストールします。

npm install aws-amplify @aws-amplify/ui-react

それぞれ下記のファイルに追記していきます。

## index.js
import Amplify from 'aws-amplify';
import awsconfig from './aws-exports';

Amplify.configure(awsconfig)
## App.js
import { withAuthenticator } from '@aws-amplify/ui-react'
...
// export default App; 削除でOK
export default withAuthenticator(App)

確認してみます。

npm start

UserPool に追加したユーザーとパスワードでサインインします。

無事できましたね。

login

ちなみに Cognito の取り込みに失敗した場合は、 amplify remove auth で最初からやり直せば OK です。

API Gateway との連携

そして、既存のAPIの利用については、API (REST) - Getting started - Amplify DocsManual Setup: Import existing REST API に記載されているものを参考にします。

見た目は雑ですが、こんな仕上がりです。

## App.js
import './App.css';
import { withAuthenticator } from '@aws-amplify/ui-react';
import Amplify, { API, Auth } from 'aws-amplify';
import React from 'react';

Amplify.configure({
  Auth: {
    region: 'ap-northeast-1',
    userPoolId: 'ap-northeast-1_xxxxxxxxxx',
    userPoolWebClientId: 'xxxxxxxxxxxxxxxx',
  },
  API: {
    endpoints: [
      {
        name: 'dev',
        endpoint:
          'https://<APIGATEWAY_ID>execute-api.ap-northeast-1.amazonaws.com/dev',
      },
    ],
  },
});

function App() {
  const handleClick = async function () {
    const user = await Auth.currentAuthenticatedUser();
    const token = user.signInUserSession.idToken.jwtToken;
    const myInit = {
      headers: {
        Authorization: token,
      },
    };

    const res = await API.get('dev', '/greeting', myInit);
    console.log(res);
  };

  return (
    <div className='App'>
      <header className='App-header'>
        <button onClick={handleClick}>Click me</button>
      </header>
    </div>
  );
}

export default withAuthenticator(App);
## index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import Amplify from 'aws-amplify';
import awsconfig from './aws-exports';

Amplify.configure(awsconfig);

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

reportWebVitals();

クリックすると、デベロッパーツールに表示されているかと思います。

success_api

ではデプロイしてみます。

アプリケーションのデプロイには、Hosting with Amplify Console と Amazon CloudFront and S3 の 2つの選択肢がありますが、本稿では特にCI/CDも必要なければ、リポジトリとの連携もいらないので、CloudFront and S3 でいきます。

amplify hosting add
? Select the plugin module to execute Amazon CloudFront and S3
? Select the environment setup: PROD (S3 with CloudFront using HTTPS)
? hosting bucket name demoapp-xxxxxxxxxx-hostingbucket
Static webhosting is disabled for the hosting bucket when CloudFront Distribution is enabled.

You can now publish your app using the following command:
Command: amplify publish
amplify publish

表示されたFQDNを押下すると、まんまとリダイレクトするので S3+CloudFrontでS3のURLにリダイレクトされてしまう場合の対処法 | DevelopersIO を参考にして Origin Domain Name を更新して確認します。

AccessDenied error でみ、見れない。。。

amazon web services - Amplify publish causes AccessDenied error - Stack Overflow の内容と同じく、CloudFront のOAIの設定に不備があるようでした。

default_CF_setting

以下のように再度 OAI を上書きをするように更新したらちゃんと見れました。

(AWSにはフィードバック済みです。)

update_CF_setting

以上です。

後片付けは忘れずに。

amplify delete

(MFAを入れ忘れて、すでに削除してくれてると思ったらしてませんでした。。。皆さんもお気を付けて。)

最後に

ホントはもうちょっと綺麗な見た目にして表示させようかな、とか諸々あったのですが、 CORS 対応で React 頑張る力は尽きていました。。

Amplify というサービスによって、フロントエンドが何だか身近な存在になってきた感が出てきました。

たまに触るフロントエンド、楽しいけど、むずかしい....(いや、どれも全部難しいんですけどね。。)

以上です。

どなたかのお役に立てば幸いです。

参考