Amplifyの提供ライブラリを使わず、他のUIライブラリで認証画面を作成してみた

2022.04.26

こんにちは。データアナリティクス事業本部 サービスソリューション部の北川です。

Amplifyは、@aws-amplify/ui-reactを提供しているので、自分でUIを作成しなくても、簡単に認証機能、画面を実装することができます。

ただ今回は、amplifyを触るのが初めてということもあり、中身を確認するためにも、他のUIライブラリを用いて認証画面を作成してみました。

amplify authの設定

Next.jsとMantine UIを使って、実装していきます。

プロジェクトの作成

$ npx create-next-app --ts

任意のプロジェクト名を付けて、移動します。

$ cd auth-sample

amplifyの環境を作成

$ amplify init
? Initialize the project with the above configuration? No
? 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: .next
? Build Command:  npm run-script build
? Start Command: npm run-script start
Using default provider  awscloudformation

? Select the authentication method you want to use: AWS profile

Distribution Directory Pathの値を.nextに変更し、他の値はデフォルトにします。

profileはamplify configureで作成したものを選択してください。初めての場合は、コンソールが開きますので手順に従ってprofileの作成をします。

認証機能を追加します。

$ amplify add auth

認証構成、認証方法、詳細設定について聞かれますので、今回は以下のように設定しました。

Using service: Cognito, provided by: awscloudformation
 
 The current configured provider is Amazon Cognito. 
 
 Do you want to use the default authentication and security configuration? Default configuration
 Warning: you will not be able to edit these selections. 
 How do you want users to be able to sign in? Username
 Do you want to configure advanced settings? No, I am done.
✅ Successfully added auth resource signsampleb4cf2245 locally

設定を反映します。

$ amplify push -y

AWS Cognitoでユーザープールを確認します。amplify consoleで、AWSのコンソール画面を開くことができます。

ユーザープールが作成されています。

フロントエンドの実装

amplifyをフロントエンドで使用できるよう、インストールします。

$ yarn add aws-amplify

UIライブラリのMantineをインストールします。

$ yarn add @mantine/hooks @mantine/form @mantine/core @mantine/next

/pages/index.tsx

import { useRouter } from "next/router";
import { FC, useEffect, useState } from "react";
import Amplify, { Auth } from "aws-amplify";
import awsconfig from "../src/aws-exports";
import { Button, Container, Group, TextInput } from "@mantine/core";
import { useForm } from "@mantine/hooks";

Amplify.configure(awsconfig);

const Home: FC = () => {
  const router = useRouter();
  const [confirm, setConfirm] = useState(false);

  const form = useForm({
    initialValues: {
      username: "",
      email: "",
      password: "",
      code: "",
    },
  });

  useEffect(() => {
    router.prefetch("/profile");
    const currentUser = async () => {
      try {
        const currentUser = await Auth.currentAuthenticatedUser();
        if (currentUser) {
          router.push("/profile");
        }
      } catch (error) {
        console.log(error);
      }
    };
    currentUser();
  }, [router]);

  const signUp = async (): Promise<void> => {
    try {
      await Auth.signUp({
        username: form.values.username,
        password: form.values.password,
        attributes: {
          email: form.values.email,
        },
      });
      setConfirm(true);
    } catch (error) {
      throw new Error();
    }
  };

  const confirmSignUp = async (): Promise<void> => {
    const password = form.values.password;
    const code = form.values.code;
    try {
      await Auth.confirmSignUp(form.values.username, code);
      await Auth.signIn({ username: form.values.username, password: password });
      router.push("/profile");
    } catch (error) {
      throw new Error();
    }
  };

  return (
    <Container style={{ width: 400, height: 200 }}>
      <h2> SignUp</h2>
      {!confirm ? (
        <form onSubmit={form.onSubmit(signUp)}>
          <TextInput
            required
            label="username"
            placeholder="your name"
            {...form.getInputProps("username")}
          />
          <TextInput
            required
            label="Email"
            placeholder="your@email.com"
            {...form.getInputProps("email")}
          />
          <TextInput
            required
            label="Password"
            {...form.getInputProps("password")}
          />
          <Group position="right" mt="md">
            <Button type="submit">SignUp</Button>
          </Group>
        </form>
      ) : (
        <form onSubmit={form.onSubmit(confirmSignUp)}>
          <TextInput required label="Code" {...form.getInputProps("code")} />
          <Group position="right" mt="md">
            <Button type="submit">Submit</Button>
          </Group>
        </form>
      )}
    </Container>
  );
};

export default Home;

confirmSignUpは、認証コードの一致を確認する関数になります。また、currentAuthenticatedUserでログインユーザーの情報を取得できます。

今回は記述しませんが、ログインの際には、signIn関数を使用します。

const signIn = async (): Promise<void> => {
  try {
    await Auth.signIn({ username: form.values.username, password: form.values.password });
    router.push("/profile");
  } catch (error) {
    throw new Error();
  }
};

簡単に、ログイン後のページも実装します。

/pages/profile

import { useRouter } from "next/router";
import { useState, useEffect, FC } from "react";
import Amplify, { Auth } from "aws-amplify";
import { CognitoUserInterface } from "@aws-amplify/ui-components";
import awsconfig from "../src/aws-exports";
import { Button, Container, Group } from "@mantine/core";

Amplify.configure(awsconfig);

const Profile: FC = () => {
  const router = useRouter();
  const [user, setUser] = useState<CognitoUserInterface | undefined>();

  useEffect(() => {
    const confirmUser = async () => {
      const currentUser = await Auth.currentAuthenticatedUser();
      if (!currentUser) {
        router.push("/");
      }
      setUser(currentUser);
    };
    confirmUser();
  }, [router]);

  const signOut = async (): Promise<void> => {
    try {
      await Auth.signOut();
    } catch (error) {
      throw new Error();
    }
    router.push("/");
  };

  return (
    <Container style={{ width: 400, height: 200 }}>
      <h2>UserName : {user?.username}</h2>
      <Group position="left" mt="md">
        <Button type="submit" onClick={signOut}>
          SignOut
        </Button>
      </Group>
    </Container>
  );
};

export default Profile;

開発環境で、機能確認をしてみます。

$ yarn dev

サインアップ画面

認証コード

サインアップ後

AWSのコンソールでも確認してみます。

ユーザーが登録されていますね。

まとめ

今回、バリデーションなどの記述はしていませんが、必要最低限の認証機能をMantine UIを使って実装することができました。 Amplifyには他にも、API、ホスティングなどの機能もあるので、今後も色々試してみたいと思います。

ではまた。