AWS Amplifyで、`withAuthenticator`と`Auth.signOut`を併用する

AWS AmplifyのSDK・ライブラリを使うと、認証系のUIや操作をほとんど自分で実装する必要がなくなります。

import type { AppProps } from 'next/app'
import Amplify from 'aws-amplify';
import { withAuthenticator } from '@aws-amplify/ui-react'
import amplifyConfig from '../src/aws-exports'

Amplify.configure({ ...amplifyConfig, ssr: true });

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <div id="container">
      <Component {...pageProps} />
    </div>
  )
}
export default withAuthenticator(MyApp)

ただし、任意のUIライブラリ(BootstrapやMaterial UIなど)でAuth系の操作を行いたい場合、「Auth.signOutを実行したのに、ログイン画面に戻らない」現象が発生します。

これは、withAuthenticatorの中で参照しているStateの更新が、Auth.signOutを実行するだけでは更新されないためです。

対処法: Hub.dispatchでイベントを手動で送る

Amplifyのui-componentでは、Hub.dispatchを使ってログインや追加認証などのイベントが送られています。そこで、内部で使われている実装と同じものを用意することで、Auth.signOut実行時にStateを正しく更新できるようにします。

まず、内部で利用されているhelperを複製します。exportしてくれていたら助かるので、そのうちGitHubに突撃しているかもしれません。

import {
    AuthState,
    UI_AUTH_CHANNEL,
    AUTH_STATE_CHANGE_EVENT,
    AuthStateHandler,
  } from '@aws-amplify/ui-components';
  import { Hub } from 'aws-amplify';

  export const dispatchAuthStateChangeEvent: AuthStateHandler = (
    nextAuthState: AuthState,
    data?: object
  ) => {
    Hub.dispatch(UI_AUTH_CHANNEL, {
      event: AUTH_STATE_CHANGE_EVENT,
      message: nextAuthState,
      data,
    });
  };
  

あとはAuth.signOutの後にこれをよびだします。

import Amplify, {Auth} from 'aws-amplify';
import { withAuthenticator } from '@aws-amplify/ui-react'
import { AuthState } from '@aws-amplify/ui-components';

...

<button onClick={() => {
  Auth.signOut({
    global: true
  }).then(() => {
    push('/')
    dispatchAuthStateChangeEvent(AuthState.SignedOut)
  })
}}>
  Logout
</button>

TypeScript的な話としては、dispatchAuthStateChangeEventの引数がAuthStateのEnumなので、@aws-amplify/ui-componentsからimportする必要があることに注意です。

似たような要領でAuthクラスのメソッドを叩いた後のイベントを起動できますので、知っておくと便利です。

Comment