成果物
サイトトップ
「ログイン」→CognitoUI→Oktaログイン画面
サイトトップに戻り(リダイレクト)、Oktaのユーザー情報が表示される。
想定する読者
- 初めてSSOするウェブアプリを作る(React)
- ↑をOKTAとCognitoで作りたい
- とりあえず動くものを作りたい
まさに自分がこの読者像でした。
成果物に辿り着くまで、色んな記事の色んなサンプルコードを組み合わせたりと苦労したので、1箇所にまとめてみようというモチベーションです。
OktaとAWS Cognitoを連携させる
物凄く長くなったので、別記事にししました。
上記で作成した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に移動して作成したユーザープールを選択する。
「アプリケーションの統合」タブを選択。
スクロールして作成したアプリケーションクライアントを選択する。
アプリケーションクライアントの画面に遷移する。
「ホストされたUI」の項目までスクロールし「編集」する。
- [許可されているコールバック URL]
- http://localhost:3000
- [許可されているサインアウト URL - オプション]
- http://localhost:3000
- [OAuth 2.0 許可タイプ]
- 認証コード付与
- [OAuth 2.0 許可タイプ]
- OpenID
- aws.cognito.signin.user.admin
OktaでCORSの許可設定をする
OktaのAPIをhttps://localhost:3000から叩けるように許可してあげる必要があります。
Oktaの管理画面に入り左側のメニューから Security > API > Trusted Originsタブを開きます。
「+ Add Origin」ボタンをクリックし下記のように入力して「Save」します。
- [Origin name]
- http://localhost:3000
- [Origin URL]
- http://localhost:3000
- 下記2項目をチェック
- Cross-Origin Resource Sharing(CORS)
- Redirect
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
実装
下記手順で進めます。
- config/auth.jsを作成
- hooks/use-auth.jsを作成
- index.jsを編集
- App.jsを編集
- App.cssを編集
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を作成
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を使えるよう設定しています。
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を編集
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 {
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にアクセスすると下図画面が表示されるはずです。
ログインすると下記のような情報を取得出来ます。
今回はattributesの中からnicknameのみ使用しています。
ちなみにattributesはCognitoの設定でOpenID 接続スコープに「aws.cognito.signin.user.admin」を追加した事によって、取得出来るようになる項目です。
おわりに
今回初めてSSOするアプリを作りました。
SSOの全体のイメージも無い状態から始めたので、シンプルなものを作るにもなかなか苦労しました。
当初は、Okta独自のコードを書いて行かないといけないんだろう、と想像していましたが、終わってみれば、フロント側でOktaを意識する事はありませんでした。
Oktaを使うCognitoでのログイン画面を実装するというが表現的には正しいでしょうか。
ここまで長かったぜー。