LoginSignup
0
1

More than 1 year has passed since last update.

Oktaを使ってReactにログイン機能を実装する

Last updated at Posted at 2023-03-26

成果物

サイトトップ
a_1.png
「ログイン」 -> Oktaのサイイン画面
a_2.png
サイトトップに戻り(リダイレクト)、Oktaのユーザー情報が表示される。
a_3.png

環境

  • React
    • ^18.2.0
  • react-router-dom
    • ^6.9.0
  • @okta/okta-react
    • ^6.7.0
  • @okta/okta-auth-js
    • ^7.2.0

※主なもののみ抜粋

想定する読者

  • Oktaのみでログイン機能を実装してみたい人
  • 次に紹介する「ハマったポイント」にハマっている人

※Okta+CognitoでReactみたいな事をしたい方は↓↓

ハマったポイント

idTokenに含まれるユーザー情報が少ない問題

事象

OktaユーザーのProfile情報が全て含まれている事を期待していたが違った。

例えば下記OktaユーザーのFirst nameとLast nameを取得したかったがidTokenの中に無かった。

↓ユーザー情報↓
a_5.png
↓idToken↓
a_4.png

解決方法

サイイン後にgetUserInfo()という関数を呼ぶ必要があった。

OktaのAPIを叩く際にCORSではねられる問題

事象

サインアウト時に下記エラーが発生してしまった。
a_6.png

解決方法

Oktaの設定画面からSecurity > API > Trusted OriginsでCORSの許可設定をする。

何やらWarningが出てる問題(動くものの)

事象

Two custom restoreOriginalUri callbacks are detected. The one from the OktaAuth configuration will be overridden by the provided restoreOriginalUri prop from the Security component.

スクリーンショット 2023-03-25 14.21.59.png

解決方法

ReactのStrictModeでコンポーネントが2回描画される事に起因する問題だった。上記リンクを参考に、useEffectのクリーンアップ関数でゴニョゴニョする。

Oktaの設定

基本設定

okta developerトップ -> 左メニュー「Applications」 -> 「Create App Integration」
o_1.png

  • [Sign-in method]
    • OIDC - OpenID Connect
  • [Application type]
    • Single-Page Application

o_2.png

  • [App integration name]
    • My React App(※任意でOK)
  • [Grant type]
    • Authorization Code
  • [Sign-in redirect URIs]
    • http://localhost:3000/login/callback
  • [Sign-out redirect URIs]
    • http://localhost:3000

o_3.png

  • Base URIs
    • 空でOK
  • Assignments
    • Skip group assignment for now

o_4.png

ユーザーをアサインする

上記で作ったサイインの仕組みを使えるOktaユーザーをアサインします。

「My React App」 -> 「Assignments」タブ -> 「Assign」ボタン -> 「Assign to People」
o_5.png
アカウントに紐付いたユーザー一覧が出てくるので、ユーザーを「Assign」します。Assignしたユーザーが、これから作るReactアプリにログイン出来るユーザーになります。
o_6.png
↓「Assign」後のイメージ↓
o_7.png

CORSの許可

OktaのAPIを叩けるドメインの設定をします。

「ハマったポイント」で紹介した「サインアウト時に下記エラーが発生してしまった」問題の原因です。

これをしないとOktaのAPIを上手く叩けずにアプリケーション側(React側)でエラーが吐き出されます。

左メニュー「Security」 -> 「API」 -> 「Trusted Origins」 タブ -> 「+ Add origin」
スクリーンショット 2023-03-26 8.54.37.png

o_10.png

  • [Origin name]
    • localhost:3000
  • [Origin URL]
    • http://localhost:3000
  • [Choose Type]
    • 下記2つをチェック
      • Cross-Origin Resource Sharing(CORS)
      • Redirect

Oktaの設定は以上になります。

Reactアプリの作成

create-react-appで作成します。

VITEなど他のツールで作成する場合は、ポートが変わるので先ほど「Add Origin」で設定したコールバックURLの変更が必要になりますので注意してください。

ご自身の作業ディレクトリでReactプロジェクトを作成します。

npx create-react-app react-okta-20230326

追加ライブラリ

上記で作成したReactプロジェクトに必要なライブラリを追加していきます。

yarn add @okta/okta-react @okta/okta-auth-js react-router-dom

ディレクトリ構成

完成品のディレクトリ構成です。

.
├── node_modules/
├── package-lock.json
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── src
│   ├── App.css
│   ├── App.js
│   ├── App.test.js
│   ├── config
│   │   └── auth.js
│   ├── hooks
│   │   └── use-auth.js
│   ├── index.css
│   ├── index.js
│   ├── logo.svg
│   ├── reportWebVitals.js
│   └── setupTests.js
└── yarn.lock

実装

下記手順で進めます。

  1. config/auth.jsを作成
  2. hooks/use-auth.jsを作成
  3. index.jsを編集
  4. App.jsを編集
  5. App.cssを編集

config/auth.jsを作成

Client ID

o_12.png

アカウントのドメイン

o_13.png

config/auth.js
// 例)0123456789abcdefghij
const CLIENT_ID = 'Client ID';
// 例)https://dev-12345678.okta.com/oauth2/default
const ISSUER = 'https://アカウントのドメイン/oauth2/default';
const REDIRECT_URI = `${window.location.origin}/login/callback`;

// eslint-disable-next-line
export default {
  oidc: {
    clientId: CLIENT_ID,
    issuer: ISSUER,
    redirectUri: REDIRECT_URI,
    scopes: [
      "openid",
      "email",
      "profile",
    ],
    pkce: true,
  }
};

hooks/use-auth.jsを作成

hooks/use-auth.js
import React, { createContext, useContext, useEffect, useState } from 'react';
import { useOktaAuth } from '@okta/okta-react';
import { OktaAuth, toRelativeUrl } from '@okta/okta-auth-js';
import config  from '../config/auth';

export const oktaAuth = new OktaAuth(config.oidc);

const authContext = createContext({});

export const ProvideAuth = ({children}) => {

  const auth = useProvideAuth();
  return (
    <authContext.Provider value={auth}>
      {children}
    </authContext.Provider>
    )
  ;
}

export const useAuth = () => {
  return useContext(authContext);
}

const useProvideAuth = () => {

  const { authState } = useOktaAuth();

  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [username, setUserName] = useState('ゲスト');
  const [user, setUser] = useState(null);

  useEffect(() => {
    if(!authState || !authState.isAuthenticated) return;
    console.log(authState);
    oktaAuth.token.getUserInfo()
      .then(e => {
        setUser(e);
        setUserName(e.name);
        setIsAuthenticated(true);
      });
  }, [authState]);

  const signIn = () => {
    const originalUri = toRelativeUrl(window.location.href, window.location.origin);
    oktaAuth.setOriginalUri(originalUri);
    oktaAuth.signInWithRedirect();
  };

  const signOut = async () => {
    oktaAuth.signOut();
    setUser(null);
    setIsAuthenticated(false);
  };

  return {
    isAuthenticated,
    user,
    username,
    signIn,
    signOut
  }
}

ポイントはuseProvideAuthuseEffectoktaAuth.token.getUserInfo()を使ってユーザー情報を取得している点です。

「ハマったポイント」で挙げた「idTokenに含まれるユーザー情報が少ない問題」の解決方法になります。

とは言え、idTokenにもユーザー名やemailなど基本的な情報は入っているので、それで事足りる場合は、上記関数を呼ばずともidTokenから取得出来ます。

ちなみに、Oktaでユーザー情報の追加は、 左メニュー「Directory」->「People」からユーザーを選択後、「Profile」タブで出来ます。
スクリーンショット 2023-03-26 9.55.34.png

index.jsを編集

index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

App.jsを編集

App.js
import './App.css';
import { useCallback, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { toRelativeUrl } from '@okta/okta-auth-js';
import { Security } from '@okta/okta-react';
import { Routes, Route } from 'react-router-dom';
import { LoginCallback } from '@okta/okta-react';

import { useAuth, oktaAuth, ProvideAuth } from './hooks/use-auth';

const Profile = () => {

  const { isAuthenticated, user, username, signIn, signOut } = useAuth();

  return (
    <>
      <main className='App'>
        <p>ようこそ{ username }さん</p>
        {
          isAuthenticated ? (
            <>
              <ul>
                { 
                  Object.keys(user)
                    .filter(key => key !== 'headers')
                    .map(key => (
                      <li key={key}> {key} : {user[key]} </li>
                    ))
                }
              </ul>
              <button onClick={() => signOut()}>ログアアウト</button>
            </>
          ) : (
            <button onClick={() => signIn()}>ログイン</button>
          )
        }
        
      </main>
    </>
  );
}

const App = () => {

  const navigate = useNavigate();
  const restoreOriginalUri = useCallback((_oktaAuth,  originalUri) => {
    navigate(toRelativeUrl(originalUri || '/', window.location.origin));
  }, [navigate])

  useEffect(() => {
    return () => {
      oktaAuth.options.restoreOriginalUri = undefined
    }
  }, [])

  return (
    <>
      <Security oktaAuth={oktaAuth} restoreOriginalUri={restoreOriginalUri}>
        <ProvideAuth>
          <Routes>
            <Route path="/" exact={true} element={<Profile />}/>
            <Route path="login/callback" element={<LoginCallback />}/>
          </Routes>
        </ProvideAuth>
      </Security>
    </>
  );

};
export default App;

ポイントはuseEffectでクリーンアップ関数を設定している点です。

「ハマったポイント」で挙げた「何やらWarningが出てる問題(動くものの)」の解決方法になります。

App.cssを編集

App.css
.App {
  background-color: #282c34;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
  color: white;
}

button {
  font-size: calc(10px + 2vmin);
}

p {
  margin-bottom: 0;
}

ul {
  list-style: none;
  font-size: 16px;
  border: 1px solid #fff;
  padding: 1em;
  margin: 1em 0;
}

以上です。

http://localhost:3000にアクセスすると下図画面が表示されるはずです。
a_1.png
ログインする際は「Oktaの設定」>「ユーザーをアサインする」でアサインしたユーザーでログインして下さい。

ログイン成功すると下記のように自分の情報が表示されるはずです。
a_3.png

おわりに

「idTokenに含まれるユーザー情報が少ない問題」の解決はちょっと大変でした。

休日に手を動かしていると、どうしても楽しようとしてしまい、何も考えずにアレやこれやと手を動かしてしまう、結果だいぶ時間を溶かした気がします。

休日でも( 業務中でも )公式ドキュメントをサボらずに読む強い心が欲しいですー。

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