LoginSignup
1
0

More than 1 year has passed since last update.

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

Last updated at Posted at 2023-03-12

成果物

サイトトップ
completion_01.png
「ログイン」→CognitoUI→Oktaログイン画面
completion_02.jpg
サイトトップに戻り(リダイレクト)、Oktaのユーザー情報が表示される。
completion_03.png

想定する読者

  • 初めてSSOするウェブアプリを作る(React)
  • ↑をOKTAとCognitoで作りたい
  • とりあえず動くものを作りたい

まさに自分がこの読者像でした。

成果物に辿り着くまで、色んな記事の色んなサンプルコードを組み合わせたりと苦労したので、1箇所にまとめてみようというモチベーションです。

OktaとAWS Cognitoを連携させる

物凄く長くなったので、別記事にししました。

OktaとAWS Cognito連携 ~SAML編~

上記で作成したOkta✖️Cognitoを使う想定で進めます。

Cognitoの設定を変更する

↑で作ったCognitoの設定を一部変更します。

[変更点]

  • 許可されているコールバックURLを変更
    • ローカルPCのReactで使うのでhttp://localhost:3000に設定
  • 許可されているサインアウトURLを追加
    • 同様の理由でhttp://localhost:3000に設定
  • OAuth2.0許可タイプを「認証コード付与」に変更
    • 「暗黙的な付与」のままだとログイン後リダイレクトする際に、URLのパラメーターに認証コードが付与されてしまいます。
    • 横取りされる危険があり、世の中的には「使ってくれるなよ」という流れのようです。
  • OpenID 接続スコープに「aws.cognito.signin.user.admin」を追加
    • ログイン後に受け取るレスポンスにattributesという項目が追加されます。
    • attributesにユーザー情報が入っています。
    • ちなみにこの設定をしないでもIDトークンからユーザー情報の取得は出来ます。ただ、ネストが深い、、、

マネコンでCognitoに移動して作成したユーザープールを選択する。

「アプリケーションの統合」タブを選択。
c_1.png
スクロールして作成したアプリケーションクライアントを選択する。
c_2.png
アプリケーションクライアントの画面に遷移する。
c_3.png
「ホストされたUI」の項目までスクロールし「編集」する。
c_4.png

  • [許可されているコールバック URL]
    • http://localhost:3000
  • [許可されているサインアウト URL - オプション]
    • http://localhost:3000

c_5.png

  • [OAuth 2.0 許可タイプ]
    • 認証コード付与
  • [OAuth 2.0 許可タイプ]
    • OpenID
    • aws.cognito.signin.user.admin

スクリーンショット 2023-03-12 9.43.55.png
「変更を保存」します。

OktaでCORSの許可設定をする

OktaのAPIをhttps://localhost:3000から叩けるように許可してあげる必要があります。

Oktaの管理画面に入り左側のメニューから Security > API > Trusted Originsタブを開きます。
スクリーンショット 2023-03-21 16.54.31.png

「+ Add Origin」ボタンをクリックし下記のように入力して「Save」します。

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

スクリーンショット 2023-03-21 17.05.40.png

スクリーンショット 2023-03-21 17.05.53.png

Reactアプリの作成

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

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

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

npx create-react-app auth-app-20230312

追加ライブラリ

aws-amplifyのみです。

このライブラリの中からCognitoのサインイン・サインアウトのための関数などを使って行きます。

yarn add aws-amplify

ディレクトリ構成

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

.
├── node_modules/
├── 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を作成

config/auth.js
// 例) ap-northeast-1
const CognitoRegion = 'リージョン'; 
//例) ap-northeast-1_ACCDEFGHI
const CognitoUserPool = 'ユーザープール ID'; 
// 例) 12345abcdefg67890hijklmnop
const CognitoUserPoolClient = 'クライアント ID'; 

// 例) okta-sso-20230301
// Cognitoのマネコン > アプリケーションの統合 > ドメイン
const CognitoDomainPrefix = 'クライアントのドメインの頭の部分'; 

const config = {
  region: CognitoRegion,
  userPoolId: CognitoUserPool,
  userPoolWebClientId : CognitoUserPoolClient,
  oauth: {
    domain: `${CognitoDomainPrefix}.auth.${CognitoRegion}.amazoncognito.com`,
    scope: ['openid','aws.cognito.signin.user.admin'],
    redirectSignIn: 'http://localhost:3000',
    redirectSignOut: 'http://localhost:3000',
    responseType: 'code',
  }
}

export default config;

ちなみにscopeやredirectSignInがCognitoで設定した情報と一致しない場合は、上手く動かないので注意して下さい。

hooks/use-auth.jsを作成

hooks/use-auth.js
import { Amplify, Auth } from 'aws-amplify';
import React, { createContext, useContext, useEffect, useState } from 'react';
import AuthConfig  from '../config/auth';

Amplify.configure({ Auth: AuthConfig });

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 [isLoading, setIsLoading ] = useState(true);
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [username, setUsrname] = useState('ゲスト');
  const [user, setUser] = useState(null);

  useEffect(() => {
    Auth.currentAuthenticatedUser()
      .then(result => {
        console.log(result);
        setUser(result.attributes);
        setUsrname(result.attributes.nickname);
        setIsAuthenticated(true);
        setIsLoading(false);
      })
      .catch(() => {
        setIsAuthenticated(false);
        setIsLoading(false);
      });

  }, []);

  const signIn = () => {
    Auth.federatedSignIn();
  };

  const signOut = async () => {
    try {
      await Auth.signOut();
      setUsrname('');
      setIsAuthenticated(false);
    } catch (error) {
      return {
        success: false,
        message: 'ログアウトに失敗しました。'
      }
    }
  };

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

}

index.jsを編集

hooks/use-auth.jsの中で作成したcontextを使えるよう設定しています。

index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { ProvideAuth } from './hooks/use-auth';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <ProvideAuth>
      <App />
    </ProvideAuth>
  </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 { useAuth } from './hooks/use-auth';
import './App.css';

function App() {

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

  return (
    <>
      <main className='App'>
        <p>ようこそ{ username }さん</p>
        {
          isAuthenticated ? (
            <button onClick={() => signOut()}>ログアアウト</button>
          ) : (
            <button onClick={() => signIn()}>ログイン</button>
          )
        }
        
      </main>
    </>
  );

}

export default App;

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);
}

以上です。

http://localhost:3000にアクセスすると下図画面が表示されるはずです。
completion_01.png
ログインすると下記のような情報を取得出来ます。

今回はattributesの中からnicknameのみ使用しています。

ちなみにattributesはCognitoの設定でOpenID 接続スコープに「aws.cognito.signin.user.admin」を追加した事によって、取得出来るようになる項目です。

console.jpg

おわりに

今回初めてSSOするアプリを作りました。

SSOの全体のイメージも無い状態から始めたので、シンプルなものを作るにもなかなか苦労しました。

当初は、Okta独自のコードを書いて行かないといけないんだろう、と想像していましたが、終わってみれば、フロント側でOktaを意識する事はありませんでした。

Oktaを使うCognitoでのログイン画面を実装するというが表現的には正しいでしょうか。

ここまで長かったぜー。

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