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 連携で手早く作成し
authTest という名前で SPA で Auth0 のアプリ作成
Settings をみると
今作った name の他に
- Domain
- Client ID
- Client Secret (ID)
が見れる。これをログイン/ログアウトボタンを React で使うときに必要とするらしい。
また Settings の下部の
Allowed
- Callback URLs
- Logout URLs
- Web Origins
という Allowed 系の URL を全て
にする。これがないとそこでログイン/ログアウトボタンが使えないと推測する。
これで Save Changes ボタンを押せば React で使えるようになるらしい。
CRA と auth0-react の導入
npx create-react-app auth0 --template typescript
CRA --temp ts で auth0 という名前で React アプリを作成
npm install @auth0/auth0-react
auth0-react のライブラリをインストールする。
.env ファイルに REACT_APP_ の環境変数を作る
ログイン機能の開発なので、開発用の認証、環境変数が必要。
Auth0 で process.env で .env ファイルから読み取る必要がある。
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
App.tsx のファイルで出力すると
function App() {
console.log(process.env.TEST);
console.log(process.env.REACT_APP_WEBSITE_NAME);
}
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";
これを使って
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
単に StrictNode で囲われている App を
<Auth0Provider
domain={process.env.REACT_APP_AUTH0_DOMAIN!}
clientId={process.env.REACT_APP_AUTH0_CLIENT_ID!}
redirectUri={window.location.origin}
>
<App />
</Auth0Provider>,
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
このように記載する。
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;
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>
);
}
App で呼び出す。
するとログインボタンが表示された。
ログインボタンをクリックしてみる
すると、Auth0 の auth0.com ドメインのログイン画面が開く。
今回は最初なのでユーザー登録をしてログインすると
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>
)
}
}
useAuth0 から logut モジュールも持ってきて
ログイン中でなければ早期リターンで ログインボタンを返すようにする。
そしてログイン中であれば、ログアウトボタンを返すようにする
ログアウトは引数にオブジェクトが必要なので
onclick={() => {logout({ key:value });} }
の形にする必要があった。
ここに returnTo: window.location.origin を入れた。
これで、ログイン時にはログアウトボタンが表示されるようになった。便利。
ユーザー情報を表示する
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;
サンプルの通り書くとログインしているけどユーザー情報が取れない処理がないので
const user: User | undefined
Object is possibly 'undefined'.ts(2532)
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
}
そしてこの三段階になった。
これでログイン中はユーザーの画像と名前とメアドが表示されて
ログアウトするとログインボタンのみが表示される仕組みが作れた。
Top comments (0)