1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Amplify UIのAuthenticatorで表示できるサインイン画面をカスタマイズしてみる

Posted at

はじめに

先日、Amplify Gen2のAuthを使って、認証機能を実装してみました。
認証画面にはAmplifyが提供するAuthenticatorを使い、デフォルトの画面を表示していました。

↓ 以下がデフォルトのサインイン画面なのですが、寂しくないですか!
image.png

というこで今回は、Authenticatorで表示できるサインイン画面をどこまカスタマイズできるか試したいと思います。

↓ 最終的にこんな感じになりました
demo5.gif

最終的なソースコードはこちら
AuthProvider.tsx
import { ThemeProvider, Authenticator, translations, useTheme, useAuthenticator } from '@aws-amplify/ui-react';
import type { Theme } from '@aws-amplify/ui-react';
import { I18n } from "aws-amplify/utils";
import { View, Flex, Image, Text, Heading, Card, Divider, Link } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';
import '../lib/amplify';
import AppIconSVG from '../assets/app_icon.svg';

I18n.putVocabularies(translations);
I18n.setLanguage('ja');

I18n.putVocabularies({
  ja: {
    'Sign In': 'ログイン',
    'Sign in': "ログイン",
    'Enter your Email': "メールアドレスを入力してください",
    'Enter your Password': "パスワードを入力してください",
    'Create Account': "新規ユーザー登録",
  },
});

const theme: Theme = {
  name: 'custom-theme',
  tokens: {
    components: {
      button: {
        primary: {
          backgroundColor: { value: '{colors.blue.60}' },
          _hover: {
            backgroundColor: { value: '{colors.purple.80}' },
          },
        },
      },
      authenticator: {
        router: {
          borderStyle: 'none',
          boxShadow: 'none',
        },
      },
    },
  },
};

export default function AuthProvider({
  children,
}: {
  children: React.ReactNode;
}) {
  
  const components = {
    // 共通ヘッダー
    Header(): any {
      const { tokens } = useTheme();
      return (
        <Card
          variation="elevated"
          borderRadius="1.5rem 1.5rem 0 0"
          display="flex"
          style={{ flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }}
        >
          <Image src={AppIconSVG} alt="App Icon" height="60px" margin={tokens.space.xs}/>
          <Heading level={3} textAlign={"center"}>
            ようこそ
          </Heading>
        </Card>
      );
    },
    // 共通フッター
    Footer() {
      const { tokens } = useTheme();
      return (
        <Card
          variation="elevated"
          borderRadius="0 0 1.5rem 1.5rem"
          padding={tokens.space.md}
          boxShadow="none"
        >
          <Text
            variation='info'
            textAlign={"center"}
            fontSize={tokens.fontSizes.xs}
          >
            © 2025 Takenoko4594. All rights reserved.
          </Text>
        </Card>
      );
    },
    SignIn: {
      // ログイン画面のヘッダー
      Header(): any {
        const { tokens } = useTheme();
        return (
          <Text
            textAlign="center"
            color={tokens.colors.font.tertiary}
          >
            アカウントにログインしてください
          </Text>
        );
      },
      // ログイン画面のフッター
      Footer() {
        const { tokens } = useTheme();
        const { toForgotPassword, toSignUp } = useAuthenticator();
        return (
          <>
            <View
              textAlign="center"
              marginTop={tokens.space.xs}
            >
              <Link
                onClick={toForgotPassword}
                
                color={tokens.colors.blue[60]}
              >
                パスワードをお忘れですか
              </Link>
            </View>
            <Divider size="small"/>
            <Flex justifyContent="center">
              <Text>アカウントをお持ちでない方は</Text>
              <Link
                onClick={toSignUp}
                color={tokens.colors.blue[60]}
              >
                新規ユーザー登録
              </Link>
            </Flex>
          </>
        );
      }
    }
  }

  const formFields = {
    signIn: {
      username: {
        isRequired: true,
      },
    },
  }

  // 画面の背景デザイン
  const AuthenticatorBackground = ({ children }: { children: React.ReactNode }) => {
    return (
      <div
        className="
          relative
          h-screen
          w-screen
          flex
          items-center
          justify-center
          overflow-hidden
          bg-gradient-to-br
          from-[#667eea]
          to-[#764ba2]
          "
      >
        <style>{`
          @keyframes float {
            0%, 100% { transform: translate(0, 0) rotate(0deg); }
            33% { transform: translate(100px, -100px) rotate(120deg); }
            66% { transform: translate(-50px, 50px) rotate(240deg); }
          }
        `}</style>
        {/* 浮遊する水玉 */}
        <div
          className="
            absolute
            -top-24
            -left-24
            w-[300px]
            h-[300px]
            rounded-full
            bg-white/10
            pointer-events-none
          "
          style={{ animation: 'float 20s ease-in-out infinite' }}
        />
        <div
          className="
            absolute
            -bottom-12
            -right-12
            w-[200px]
            h-[200px]
            rounded-full
            bg-white/10
            pointer-events-none
          "
          style={{ animation: 'float 20s ease-in-out infinite', animationDelay: '5s' }}
        />
        <div
          className="
            absolute
            top-1/2
            right-[10%]
            w-[150px]
            h-[150px]
            rounded-full
            bg-white/10
            pointer-events-none
          "
          style={{ animation: 'float 20s ease-in-out infinite', animationDelay: '10s' }}
        />
        {children}
      </div>
    );
  }

  const AuthenticatorStyleWrapper = ({ children }: { children: React.ReactNode }) => {
    return (
      <div
        className='
          [&_[role="tablist"]]:!hidden
          [&_div[data-amplify-router-content]]:!py-0
          [&_div[data-amplify-container]]:!shadow-2xl
        '
      >
        {children}
      </div>
    );
  }

  return (
    <ThemeProvider theme={theme}>
      <AuthenticatorBackground>
        <AuthenticatorStyleWrapper>
          <Authenticator
            formFields={formFields}
            components={components}
          >
            {children}
          </Authenticator>
        </AuthenticatorStyleWrapper>
      </AuthenticatorBackground>
    </ThemeProvider>
  );
}

概要

Auth機能を使えばサクッと認証機能が実装できる

Amplify Gen2には、Authという機能があります。
Authを使用することで、TypeScriptのコードから簡単にAmazon Cognitoをデプロイできます。

以下のコードはAmplifyのセットアップ時に作成されますが、このコードだけでAmazon Cognitoのユーザープールも作成されます。

例:デフォルト設定
import { defineAuth } from "@aws-amplify/backend"

export const auth = defineAuth({
  loginWith: {
    email: true,
  },
})

フロントエンドとの連携もバッチリサポート

Amplifyは、フロントエンドとAuthで構築したAmazon Cognitoとの連携もサポートしています。
フロントエンド側では、Amplifyが提供するAuthenticatorを組み込むことで、簡単に連携が完了します。これで先ほどのデフォルトのサインイン画面を表示出来ちゃいます。

例:フロントエンド側の設定
import { Authenticator } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';

export default function AuthProvider({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <Authenticator>
      {children}
    </Authenticator>
  );
}

だけど、デフォルトの画面だと味気ない...
ということで今回いろいろとカスタマイズしてみよう!という話になります。

やってみる

前提

  • フロントエンドはVite + React + TypeSciptで構築します
  • デザインは一部tailiwindcssを使ってスタイルを指定しています
  • 既にAuth機能でAmazon Cognitoがセットアップ済み状態をスタートとします

カスタム1. 表示言語を日本語化したい

Authenticatorの表示言語は、デフォルトだと英語で表示されます。
これを日本語に変更します。

日本語化後の画面
image.png

Amplify UIは、多言語対応のためI18nという機能を提供しています。
I18nの設定を変更すれば、日本語でも表示できます。

AuthProvider.tsx
+ import { Authenticator, translations } from '@aws-amplify/ui-react'; // translationsを追加
+ import { I18n } from "aws-amplify/utils";
import '@aws-amplify/ui-react/styles.css';
import '../lib/amplify';

+ I18n.putVocabularies(translations);
+ I18n.setLanguage('ja');

export default function AuthProvider({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <Authenticator>
      {children}
    </Authenticator>
  );
}

カスタム2:日本語の表示文言を変更したい

日本語には出来たけど、文言が気に入らない...
そんなときはI18nを使用すれば文言も変更できます。

文言変更後の画面
image.png

I18n.putVocabulariesを設定することで、任意の文言に上書きすることが出来ます。

AuthProvider.tsx
import { Authenticator, translations } from '@aws-amplify/ui-react';
import { I18n } from "aws-amplify/utils";
import '@aws-amplify/ui-react/styles.css';
import '../lib/amplify';

I18n.putVocabularies(translations);
I18n.setLanguage('ja');

+ I18n.putVocabularies({
+   ja: {
+     'Sign In': 'ログイン',
+     'Sign in': "ログイン",
+     'Enter your Email': "メールアドレスを入力してください",
+     'Enter your Password': "パスワードを入力してください",
+     'Create Account': "新規ユーザー登録",
+   },
+ });

export default function AuthProvider({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <Authenticator>
      {children}
    </Authenticator>
  );
}

putVocabulariesで変更できるキー(日本語向け)の一覧は以下になります。
ここから変更したい文言のキーを探して、任意に変更します。

カスタム3. 入力フィールドをカスタムしたい

サインイン画面は、入力フィールドにメールアドレスとパスワードがあります。
この入力フィールドをカスタマイズしたいことがあると思います。

そんなときはAuthenticatorformFieldsプロパティで変更できます。

今回は試しにメールアドレスを必須入力に設定してみました。

AuthProvider.tsx
  const formFields = {
    signIn: {
      username: {
        isRequired: true,
      },
    },
  }

あとはreturnに追加してあげると、画面にも反映されます。

AuthProvider.tsx
// 省略
  return (
    <Authenticator
+     formFields={formFields}
    >
      {children}
    </Authenticator>
  );
// 省略

他に変更できること

詳細はこちら

実装(Authenticator.d.ts、form.d.ts)を見たところ、以下の項目をカスタマイズできるようです。

変更項目 説明
defaultValue HTMLの defaultValue 属性に対応。入力フィールドの初期値を設定するために使用します。
isReadOnly HTMLの readonly 属性に対応。入力フィールドを読み取り専用にする場合に使用します。
pattern HTMLの pattern 属性に対応。入力値の形式を正規表現で制限するために使用します。
minLength HTMLの minLength 属性に対応。入力値の最小文字数を指定します。
maxLength HTMLの maxLength 属性に対応。入力値の最大文字数を指定します。
labelHidden true に設定すると、入力フィールドの上に表示されるラベルを非表示にします。
label 入力フィールドに表示するラベルのテキストを指定します。
placeholder 入力フィールドに表示するプレースホルダー(入力例など)を指定します。
required(非推奨) 非推奨のプロパティ。内部利用向けで、代わりに isRequired を使用してください。
isRequired このフィールドがフォーム送信時に必須かどうかを指定します。
dialCode 電話番号入力時のデフォルトの国コード(例:+81)を指定します。
totpIssuer TOTP(Time-based One-Time Password)QRコードのセットアップ時に使用される発行者名を指定します。
totpUsername TOTP QRコードのセットアップ時に使用されるユーザー名を指定します。
dialCodeList 電話番号入力フィールドで表示する国コードのリストを指定します。
order このフィールドの表示順序を指定する整数値です。
type HTMLの input タグの type 属性に対応。例:text, email, password など。
autocomplete HTMLの autocomplete 属性に対応。ブラウザによる自動入力の挙動を制御します。
autocapitalize 入力の最初の文字を自動的に大文字にするかどうかを指定します。

フィールド名を変更したい場合は、以下の一覧にあるものは変更できるようです。
https://ui.docs.amplify.aws/react/connected-components/authenticator/customization#input-form-field-names-table

カスタム4. 共通なヘッダーとフッターを追加したい

Authenticatorの基本的な画面構成ですが、以下のヘッダー・フッターがあります。

  • 認証画面間で共通のヘッダー・フッター (下図の赤枠)
  • 機能固有のヘッダー・フッター (下図の黄枠)

まずは認証画面間で共通のヘッダーとフッターを追加します。

image.png

ヘッダーとフッター追加した画面
※アイコン画像は生成AIで生成してます。
image.png

Authenticatorは、componentsプロパティを指定することで、ヘッダーやフッターを自由にカスタマイズすることが出来ます。
以下のようにcomponentsに渡すヘッダー・フッターを定義します。

AuthProvider.tsx
// 省略

const components = {
  // 共通ヘッダー
  Header(): any {
    const { tokens } = useTheme();
    return (
      <Card
        variation="elevated"
        borderRadius="1.5rem 1.5rem 0 0"
        display="flex"
        style={{
            flexDirection: "column",
            alignItems: "center",
            justifyContent: "center"
          }}
      >
        <Image
          src={AppIconSVG}
          alt="App Icon"
          height="60px"
          margin={tokens.space.xs}
        />
        <Heading
          level={3}
          textAlign="center"
        >
          ようこそ
        </Heading>
      </Card>
    );
  },
  Footer() {
    const { tokens } = useTheme();
    return (
      <Card
        variation="elevated"
        borderRadius="0 0 1.5rem 1.5rem"
        padding={tokens.space.md}
        boxShadow="none"
      >
        <Text
          variation="info"
          textAlign="center"
          fontSize={tokens.fontSizes.xs}
        >
          © 2025 Takenoko4594. All rights reserved.
        </Text>
      </Card>
    );
  },
}

// 省略

あとはreturnに追加してあげると、画面にも反映されます。

AuthProvider.tsx
// 省略
  return (
    <Authenticator
      formFields={formFields}
+     components={components}
    >
      {children}
    </Authenticator>
  );
// 省略

Amplify UIについて
Amplify UIは、Amplifyが提供するUIライブラリです。
このライブラリを使って構築することで、一貫性のあるUIデザインやUXを実現することが出来ます。

今回の実装では、CardTextHeadingImageといったUIコンポーネントを使っています。

参考:https://ui.docs.amplify.aws/react/components

const { tokens } = useTheme();の補足
Amplify UIは、色・フォント・間隔などのスタイル要素をデザイントークンとして定義されています。
useThemeフックからデザイントークンへアクセスすることができ、統一したスタイルにすることが出来ます。

例えば、以下のようにフォントサイズをトークンから設定できます。

fontSize={tokens.fontSizes.xs}

参考:https://ui.docs.amplify.aws/react/theming

カスタム5. サインイン画面用のヘッダーとフッターを変更したい

次はサインイン画面専用のヘッダー・フッターを変更してみます。

ヘッダー・フッター追加後の画面
image.png

先ほどのcommponentsの設定を追加します。

AuthProvider.tsx
// 省略

const components = {
  // 共通ヘッダー・フッターは省略
+ SignIn: {
+   // ログイン画面のヘッダー
+   Header(): any {
+     const { tokens } = useTheme();
+     return (
+       <Text
+         textAlign="center"
+         color={tokens.colors.font.tertiary}
+       >
+         アカウントにログインしてください
+       </Text>
+     );
+   },
+   // ログイン画面のフッター
+   Footer() {
+     const { tokens } = useTheme();
+     const { toForgotPassword, toSignUp } = useAuthenticator();
+     return (
+       <>
+         <View
+           textAlign="center"
+           marginTop={tokens.space.xs}
+         >
+           <Link
+             onClick={toForgotPassword}
+             color={tokens.colors.blue[60]}
+           >
+             パスワードをお忘れですか
+           </Link>
+         </View>
+         <Divider size="small"/>
+         <Flex justifyContent="center">
+           <Text>アカウントをお持ちでない方は</Text>
+           <Link
+             onClick={toSignUp}
+             color={tokens.colors.blue[60]}
+           >
+             新規ユーザー登録
+           </Link>
+         </Flex>
+       </>
+     );
+   }
+ }
}

// 省略

カスタム6. 自前のUIでサインイン画面をカスタムしたい

例えば、デフォルトのボタンは使いたくない、自由にUIをカスタムしたい!といった場合もあるのではないでしょうか。
そんなときはuseAuthenticatorフックが使えます。

const { toForgotPassword, toSignUp } = useAuthenticator();

このフックは、任意の認証機能(サインイン、サインアップ、パスワード再設定など)への画面遷移を関数として呼び出すことができ、現在の認証状態やユーザー情報(例:メールアドレス)にもアクセスできます。他にもやれることはモリモリです。

今回は、toForgotPasswordtoSignUpを使って、パスワード再設定画面とサインアップ画面への遷移関数を呼び出しています。

<Link
  onClick={toSignUp}
  color={tokens.colors.blue[60]}
>
  新規ユーザー登録
</Link>

カスタム7. 切り替えタブを非表示にしたい (無理やり)

デフォルトだと、サインイン画面とサインアップ画面を切り替えるタブが表示されてます。
image.png

今回は、カスタム6で実装した自前のボタンを使って切り替えるので、個のタブを非表示にしてみます。

切り替えタブを非表示後の画面
image.png

やり方は大きく2つあります。

  • 1つ目は、サインアップ機能ごと非表示にする方法です
  • 2つ目は、CSSで無理やり非表示にする方法です

1は、AuthenticatorのプロパティにhideSignUpフラグを指定するだけなのですが、今回はサインアップ画面が使えなくなるのは困るので、2つめの方法で非表示にします。

↓ 1つ目の方法は以下を参照

ブラウザの開発者ツールを開き、切り替えタブの

タグを探します。

image.png

本当はclassで指定したかったのですが上手く適用されなかったので、roleに対して非表示のスタイルを設定しています。
(以下は、tailwindcssの指定なので、素のCSSならdisplay: noneを指定が必要です)

// 省略

  const AuthenticatorStyleWrapper = ({ children }: { children: React.ReactNode }) => {
    return (
      <div
        className='
+         [&_[role="tablist"]]:!hidden
          [&_div[data-amplify-router-content]]:!py-0
          [&_div[data-amplify-container]]:!shadow-2xl
          [&_div[data-amplify-container]]:!rounded-2xl
        '
      >
        {children}
      </div>
    );
  }
// 省略
共通のデザインテーマを変更したい場合

先ほどAmplify UIには、デザイントークンというものがあり、useThme()から引き出せるという説明をしました。
このデザイントークンは、Amplify UIの方でデフォルト値が定義されているのですが、ThemeProviderを使用して任意にカスタマイズできます。

↓ 以下はボタンの色を変えてみてます。
image.png

const theme: Theme = {
  name: 'custom-theme',
  tokens: {
    components: {
+     button: {
+       primary: {
+         backgroundColor: { value: '{colors.blue.60}' },
+         _hover: {
+           backgroundColor: { value: '{colors.purple.80}' },
+         },
+       },
+     },
    },
  },
};
</details>

参考:https://ui.docs.amplify.aws/react/theming

その他. 背景のデザインを変更する

最後に背景のデザインを変更します。

背景を追加版
demo5.gif

ただし、Authenticatorコンポーネントはあくまで中央のパネル部分のみを構成するUI部品です。そのため、背景全体のデザインを変更したい場合は、別で設定する必要があります。

ということで今回は、tailwindcssを使って、背景デザイン用コンポーネントを自作します。
スタイルさえ適用できればやり方は問わないので、あくまで参考程度で。

背景デザイン用コンポーネント ソースコード
AuthProvider.tsx
// 省略

// 画面の背景デザイン
const AuthenticatorBackground = ({ children }: { children: React.ReactNode }) => {
  return (
    <div
      className="
        relative
        h-screen
        w-screen
        flex
        items-center
        justify-center
        overflow-hidden
        bg-gradient-to-br
        from-[#667eea]
        to-[#764ba2]
        "
    >
      <style>{`
        @keyframes float {
          0%, 100% { transform: translate(0, 0) rotate(0deg); }
          33% { transform: translate(100px, -100px) rotate(120deg); }
          66% { transform: translate(-50px, 50px) rotate(240deg); }
        }
      `}</style>
      {/* 浮遊する水玉 */}
      <div
        className="
          absolute
          -top-24
          -left-24
          w-[300px]
          h-[300px]
          rounded-full
          bg-white/10
          pointer-events-none
        "
        style={{ animation: 'float 20s ease-in-out infinite' }}
      />
      <div
        className="
          absolute
          -bottom-12
          -right-12
          w-[200px]
          h-[200px]
          rounded-full
          bg-white/10
          pointer-events-none
        "
        style={{ animation: 'float 20s ease-in-out infinite', animationDelay: '5s' }}
      />
      <div
        className="
          absolute
          top-1/2
          right-[10%]
          w-[150px]
          h-[150px]
          rounded-full
          bg-white/10
          pointer-events-none
        "
        style={{ animation: 'float 20s ease-in-out infinite', animationDelay: '10s' }}
      />
      {children}
    </div>
  );
}


// 省略

作成した背景デザイン用コンポーネントをretrunに追加すると、画面に反映されます。

AuthProvider.tsx
// 省略

  return (
    <ThemeProvider theme={theme}>
+     <AuthenticatorBackground>
        <AuthenticatorStyleWrapper>
          <Authenticator
            formFields={formFields}
            components={components}
          >
            {children}
          </Authenticator>
        </AuthenticatorStyleWrapper>
+     </AuthenticatorBackground>
    </ThemeProvider>
  );

まとめ

今回は、AWS AmplifyのAuthenticatorで表示できるサインイン画面を好き勝手にカスタマイズしてみました。

Goodポイント

設定が充実しているので、フロントエンドに詳しくなくとも、設定値を把握すればある程度の実装はできる印象でした。

Amplifyのコンセプト自体が「フロントエンド開発者でもバックエンドを簡単に扱える」だと思うので、Amazon Cognitoを意識せずに実現できるのは良いですね。

Badポイント

逆にAuthenticator独特の設定がモリモリなので、慣れていない人は苦戦しそうなイメージです。

あと調べても意外と使い込んでる人もあまりいないので、情報が少ないのも難点かもです。

最後に

どうやら他にも、Amazon Cognito「マネージドログイン」など認証画面を実現できる機能があるようなので、今度はそちらも試してみたいと思います。

1
2
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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?