LoginSignup
5
5

More than 3 years have passed since last update.

Amplify で GraphQL と Cognito 認証

Posted at

概要

はじめての Amplify デプロイまで の続きです。
前回のプロジェクト作成時点で認証に Cognito を使うようにしたり、mock で必要な設定を docker に入れています。
Cognito によるユーザー認証と GraphQL との連携を実装します。

ユーザー認証機能の追加

yarn add aws-amplify aws-amplify-react

App.tsx を以下のように変更します。
アカウント作成時に電話番号を不要にするために withAuthenticator の signUpConfig に hiddenDefaults を追加しています。

import React from 'react';
import Amplify from '@aws-amplify/core';
import awsmobile from './aws-exports';
import { withAuthenticator } from 'aws-amplify-react';
import '@aws-amplify/ui/dist/style.css';

Amplify.configure(awsmobile)

const App: React.FC = () => {
  return (
    <>
      <h1>タイトル</h1>
    </>
  )
}

//@ts-ignore
export default withAuthenticator(App, {
  signUpConfig: {
    hiddenDefaults: ['phone_number']
  }
});

※ import '@aws-amplify/ui/dist/style.css'; がないと認証画面のスタイルがない状態になるので必要でした
スクリーンショット 2020-07-05 10.33.51.pngスクリーンショット 2020-07-05 10.34.19.png

ログイン画面から Create account でアカウントを作成し、メールの認証をするとログインができます。
「タイトル」が表示されます。

GraphQL API の作成

まずは自動生成された schema.graphql の TODO モデルを試してみます。

schema.graphql
type Todo @model {
  id: ID!
  name: String!
  description: String
}

mock を利用することでローカルで検証ができます。

$ amplify mock api

http://localhost:20002 にアクセスすると以下のように表示されていると思います。
スクリーンショット 2020-07-05 10.53.48.png

TODO の作成
以下のように createTodo を実行しました。
スクリーンショット 2020-07-05 10.55.59.png

作成した TODO を取得してみます。
スクリーンショット 2020-07-05 10.57.37.png

react と GraphQL の連携

作成したデータを画面に表示してみます。

今回は docker でやっているので aws-exports.js の aws_appsync_graphqlEndpoint を
以下のように localhost に書き換えが必要です(ローカル開発中だけ)

aws-exports.js
"aws_appsync_graphqlEndpoint": "http://localhost:20002/graphql",

App.tsx を以下のように変更します。
Todo に保存したデータの1つを表示します。

App.tsx
import React, {useState} from 'react'
import Amplify from '@aws-amplify/core'
import awsmobile from './aws-exports'
import {withAuthenticator} from 'aws-amplify-react'
import '@aws-amplify/ui/dist/style.css'
import {listTodos} from './graphql/queries'
import API, {graphqlOperation} from '@aws-amplify/api'
import { Auth } from 'aws-amplify';

Amplify.configure(awsmobile)

const App: React.FC = () => {
  const [todos, setTodos] = useState<any>({})

  const getTodo = async() => {
    try {
      const res: any = await API.graphql(graphqlOperation(listTodos))
      console.log(res)
      setTodos(res.data.listTodos.items)
    } catch (e) {
      console.log(e)
    }
  }

  const ShowTodo = () => {
    if (todos.length > 0) {
      const todo = todos[0]
      return (
        <>
          <p>{ todo.id }</p>
          <p>{ todo.name }</p>
          <p>{ todo.description }</p>
          <p>{ todo.createdAt }</p>
          <p>{ todo.updatedAt }</p>
        </>
      )
    }
    return (
      <></>
    )
  }

  return (
    <>
      <h1>Todos</h1>
      <button onClick={() => getTodo()}>get todos!</button>
      <div>
        <ShowTodo/>
      </div>
    </>
  )
}
...

get todos! ボタンを押すと下に取得したデータが表示されます。
スクリーンショット 2020-07-05 15.17.03.png

cognito group による制御

AWS コンソールの Cognito で、グループを作成し、最初のユーザー認証で作成したアカウントに member グループを割り当てます。
スクリーンショット 2020-07-05 15.30.45.png

schema.graphql を以下のように変更します。

schema.graphql
type Todo
  @model
  @auth(rules: [
    { allow: owner },
    { allow: groups, groups: ["admin"], operations: [read, update] }
  ])
{
    id: ID!
    name: String!
    description: String
}

これにより、member 権限のアカウントは Todo に対してなにもできなくなります。
一度ログアウトするために App.tsx を以下のように変更します。

App.tsx
import React, {useState} from 'react'
import Amplify from '@aws-amplify/core'
import awsmobile from './aws-exports'
import {withAuthenticator} from 'aws-amplify-react'
import '@aws-amplify/ui/dist/style.css'
import {listTodos} from './graphql/queries'
import API, {graphqlOperation} from '@aws-amplify/api'
import { Auth } from 'aws-amplify';

Amplify.configure(awsmobile)

const App: React.FC = () => {
  const [todos, setTodos] = useState<any>({})

  const signOut = () => {
    Auth.signOut()
  }

  const getTodo = async() => {
    try {
      const res: any = await API.graphql(graphqlOperation(listTodos))
      console.log(res)
      setTodos(res.data.listTodos.items)
    } catch (e) {
      console.log(e)
    }
  }

  const ShowTodo = () => {
    if (todos.length > 0) {
      const todo = todos[0]
      return (
        <>
          <p>{ todo.id }</p>
          <p>{ todo.name }</p>
          <p>{ todo.description }</p>
          <p>{ todo.createdAt }</p>
          <p>{ todo.updatedAt }</p>
        </>
      )
    }
    return (
      <></>
    )
  }

  return (
    <>
      <button onClick={ () => signOut() }>ログアウト</button>
      <h1>Todos</h1>
      <button onClick={() => getTodo()}>get todos!</button>
      <div>
        <ShowTodo/>
      </div>
    </>
  )
}
...

以下のような画面になります。
一度ログアウトをすると、ログイン画面に遷移するので再度ログインします。
get todos! ボタンを押しても表示は変わらず、データが0件であることがわかります。
スクリーンショット 2020-07-05 20.09.15.png
例えば、ここで schema.graphql の groups を admin から member に変更すると、データが取得できることを確認できます。

また、画面表示をグループごとに分けたいので、App.tsx に以下を追加しました。
初回でログインユーザーのグループ情報を取得し、その情報をもとに表示を出し分けました。
useEffect で初回のみユーザー情報を取得します(第2引数に [] を入れないと無限ループします)

App.tsx
  const [group, updateGroup] = useState<any>({})

  const getUser = async() => {
    const user =  await Auth.currentAuthenticatedUser();
    updateGroup(user.signInUserSession.accessToken.payload["cognito:groups"])
    console.log(user.signInUserSession.accessToken.payload["cognito:groups"])
  }

  useEffect(() => {
    getUser()
  }, [])

  return (
    <>
      {
        group === 'admin' ? (
          <h1>管理者画面</h1>
        ) : (
          <h1>メンバー画面</h1>
        )
      }
      <button onClick={ () => signOut() }>ログアウト</button>
      <h1>Todos</h1>
      <button onClick={() => getTodo()}>get todos!</button>
      <div>
        <ShowTodo/>
      </div>
    </>
  )

スクリーンショット 2020-07-05 19.54.16.png

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5