DEV Community

kaede
kaede

Posted on

React TypeScript Auth0 でログイン/ログアウト とユーザー情報の表示を実装する

why

https://www.becomebetterprogrammer.com/jwt-authentication-middleware-nodejs-typescript/

認証機能のあるブログサービスを作ろうと思い、調べてみたら
Express で作るのがかなり難しそうに見えた

そんな時、Auth0 の外部サービス使えば?と聞き、調べると

https://dev.classmethod.jp/articles/add-authentication-with-auth0-to-react-typescript-app/

Auth0 側の管理画面でボタン押して作って
React コンポーネントに組み込んで
提供されるログインボタン押せば Auth0 側のモーダルに行くので、簡単そうに見えた。

Auth0 のアプリ作成

アカウントを GitHub 連携で手早く作成し

Image description

authTest という名前で SPA で Auth0 のアプリ作成

Settings をみると

今作った name の他に

  • Domain
  • Client ID
  • Client Secret (ID)

が見れる。これをログイン/ログアウトボタンを React で使うときに必要とするらしい。

また Settings の下部の

Allowed

  • Callback URLs
  • Logout URLs
  • Web Origins

という Allowed 系の URL を全て

http://localhost:3000/

にする。これがないとそこでログイン/ログアウトボタンが使えないと推測する。

これで Save Changes ボタンを押せば React で使えるようになるらしい。


CRA と auth0-react の導入

npx create-react-app auth0 --template typescript
Enter fullscreen mode Exit fullscreen mode

CRA --temp ts で auth0 という名前で React アプリを作成

npm install @auth0/auth0-react
Enter fullscreen mode Exit fullscreen mode

auth0-react のライブラリをインストールする。


.env ファイルに REACT_APP_ の環境変数を作る

ログイン機能の開発なので、開発用の認証、環境変数が必要。

Auth0 で process.env で .env ファイルから読み取る必要がある。

https://qiita.com/yuta-ushijima/items/a653e9ca4847276f19cd#%E6%94%B9%E4%BF%AE%E5%BE%8C%E3%81%AE%E3%82%B3%E3%83%BC%E3%83%89

proccess.env については CRA で React App を作った時点で
dotenv ライブラリが組み込まれるので

https://stackoverflow.com/questions/53237293/react-evironment-variables-env-return-undefined

REACT_APP_ から変数名を始めれば .env ファイルから読み取れた。
普通に TEST=testString にしても読み取れなかった。

.env ファイルにこう書いて

TEST=TEST_STRING
REACT_APP_WEBSITE_NAME=hello
Enter fullscreen mode Exit fullscreen mode

App.tsx のファイルで出力すると

function App() {
console.log(process.env.TEST);
console.log(process.env.REACT_APP_WEBSITE_NAME);
}
Enter fullscreen mode Exit fullscreen mode

undefined
hello

ちゃんと REACT_APP_ がついている変数のみ読み取れた。


index で AuthProvider で App を囲い、.env の ドメインとクライアントID を紐づける。

Redux でも Provider で App を囲うのは共通だった。

https://qiita.com/G-awa/items/fa34fd7065ef1e14d3d9#auth0provider

import { Auth0Provider } from "@auth0/auth0-react";
Enter fullscreen mode Exit fullscreen mode

これを使って

root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
Enter fullscreen mode Exit fullscreen mode

単に StrictNode で囲われている App を

  <Auth0Provider
    domain={process.env.REACT_APP_AUTH0_DOMAIN!}
    clientId={process.env.REACT_APP_AUTH0_CLIENT_ID!}
    redirectUri={window.location.origin}
  >
    <App />
  </Auth0Provider>,
Enter fullscreen mode Exit fullscreen mode

Auth0Provider で囲い

先ほど取得した Auth0 のドメインID と クライアントID をセットして
リダイレクトにはルート?をセットする。

最後に! をつけているので、モノがなくてもエラーにはならないと推測
だが、外すと String に String|Undef は入らないとエラーになる
なので name?:string のような文法なのかもしれない

それはともかく Auth0 の ID の変数には ! を末尾につける

.env ファイルには

REACT_APP_AUTH0_DOMAIN=dev-123abc.us.auth0.com
REACT_APP_AUTH0_CLIENT_ID=123aBcD
Enter fullscreen mode Exit fullscreen mode

このように記載する。

Redux ではこの段階で console にストアのメッセージが出ていたが
Auth0 では Provider を紐づけただけでは何のメッセージもない。


ログインボタンを isAuthenticated, loginWithRedirect で作って App から呼び出す

auth0-react のライブラリから isAuthenticated, loginWithRedirect の モジュールを使ってログインボタンのコンポーネントを作る。

https://qiita.com/G-awa/items/fa34fd7065ef1e14d3d9#loginbutton

import { useAuth0 } from "@auth0/auth0-react";

function LoginButton() {
  const { isAuthenticated, loginWithRedirect } = useAuth0();

  return !isAuthenticated ? (
    <button onClick={loginWithRedirect}>Log in</button>
  ) : null;
}

export default LoginButton;
Enter fullscreen mode Exit fullscreen mode

useAuth0 を import
Auth0 で認証が済んでいるかの isAuthenticated
Auth0 のログインモーダルを開く関数の loginWithRedirect

これらを useAuth から作成

isAuthenticated でない、つまりログインされていないときには
クリックしたときに loginWithRedirect を発火させるボタンを描画

isAuthenticated である、つまりログインされているときは
null を描画

するボタンを作り

import LoginButton from './LoginButton';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <LoginButton/>
      </header>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

App で呼び出す。

Image description

するとログインボタンが表示された。


ログインボタンをクリックしてみる

Image description

すると、Auth0 の auth0.com ドメインのログイン画面が開く。

今回は最初なのでユーザー登録をしてログインすると

Image description

localhost 3000 の画面に戻り、ログインボタンが消える。


ログイン中はログアウトボタンが表示されるようにする

ログアウトボタンは少し複雑で、returnTo でログアウト後にユーザーが送られる場所を指定するオブジェクトを渡す必要がある。

このログアウトボタンを、ログイン中のみ表示するようにしてみる。

function LoginButton() {
  const { isAuthenticated, loginWithRedirect, logout } = useAuth0();
  if ( !isAuthenticated ) {
    return (
      <button onClick={loginWithRedirect}>LOG IN</button>
    )
  } else {
    return (
      <button
        onClick={() => {
          logout({ returnTo: window.location.origin });
        }}
      >LOG OUT</button>
    )
  }
}
Enter fullscreen mode Exit fullscreen mode

useAuth0 から logut モジュールも持ってきて
ログイン中でなければ早期リターンで ログインボタンを返すようにする。
そしてログイン中であれば、ログアウトボタンを返すようにする

ログアウトは引数にオブジェクトが必要なので

onclick={() => {logout({ key:value });} }
Enter fullscreen mode Exit fullscreen mode

の形にする必要があった。

ここに returnTo: window.location.origin を入れた。

Image description

これで、ログイン時にはログアウトボタンが表示されるようになった。便利。


ユーザー情報を表示する

Auth 0 の ユーザーしか見れないページの

Show User Profile Information の項目にある。

import React from "react";
import { useAuth0 } from "@auth0/auth0-react";

const Profile = () => {
  const { user, isAuthenticated, isLoading } = useAuth0();

  if (isLoading) {
    return <div>Loading ...</div>;
  }

  return (
    isAuthenticated && (
      <div>
        <img src={user?.picture} alt={user?.name} />
        <h2>{user.name}</h2>
        <p>{user.email}</p>
      </div>
    )
  );
};

export default Profile;
Enter fullscreen mode Exit fullscreen mode

サンプルの通り書くとログインしているけどユーザー情報が取れない処理がないので

const user: User | undefined
Object is possibly 'undefined'.ts(2532)
Enter fullscreen mode Exit fullscreen mode

user の オブジェクトが undef かもしれないエラーが出てしまう
なので、user がある時だけ描画される条件にしたら 型エラーが消えた。
TS でのエラーは type だけでない、例外処理がされてない場合も出るようだ。

ないと動かなくなるものでは例外処理大事!

また、ローディング中でなく、ログイン済みでもない場合の処理もないと
undefined の場合があるからとエラーが出てしまう。

なのでその時は null を返す処理も入れる。

  if (isLoading) {
    return <div>Loading ...</div>;
  } else if (isAuthenticated && user) {
    return (
      <div>
        <img src={user.picture} alt={user.name} />
        <h2>{user.name}</h2>
        <p>{user.email}</p>
      </div>
    );
  }
  else {
    return null
  }
Enter fullscreen mode Exit fullscreen mode

そしてこの三段階になった。

Image description

これでログイン中はユーザーの画像と名前とメアドが表示されて

Image description

ログアウトするとログインボタンのみが表示される仕組みが作れた。

Top comments (0)