前回はAuth0が提供するライブラリ(@auth0/auth0-react)を使ってログイン認証を実装しましたが、今回は一般的なOpenID Connect用APIであるreact-oidcを使ってログイン認証処理を実装してみます。

~目次~

Auth0の定義追加

新Application(AuthSample2)を追加

react-oidcを実装したサンプルプログラムを実装するにあたり、Auth0の定義を若干修正する必要があります。

ですので、前回Auth0上で作成したApplication(AuthSample1)とは別に、新しいApplication(AuthSample2)を作ります。

 

メニューから「Getting Started」を選択します。そして「Getting Started」画面の「Integrate Auth0 into your application」から「Create Application→」をクリックします。

 

「Create Application」画面が表示されます。「Name」は「AuthSample2」とします。続けて「application type」を選びます。前回同様に「Single Page Web Applications」を選びます。

最後に「Create」ボタンを押します。

 

「AuthSample2」の設定画面が表示されます。「Settings」タブを選択して設定を進めます。

基本的に「AuthSample1」と同じ設定をしていきますが、「Allowed Callback URLs」は前回とは異なり「http://localhost:3000/authentication/callback」とします。

 

最後に「Save Changes」で設定を保存します。

 

最終的にはこんな感じでAuthSample1とAuthSample2ができることになります。

 

react-oidcとは

JavaScriptでOpenID Connect (OIDC)やOAuth2プロトコルを実装するライブラリである IdentityModel/oidc-client-js (https://github.com/IdentityModel/oidc-client-js)というオープンソースがベースになっています。
2021年8月時点では、もう自分ではメンテできない!と宣言されていてバージョンアップが期待できないオープンソースですが、このoidc-client-jsを元にさまざまな派生プログラムが作られています。
 
今回使うAxaGuilDEv/react-oidcもoidc-client-jsを使用したプログラムのひとつで、ReactでOpenID Connectを実装するための機能を提供しています。
React用のOpenID Connectライブラリは多々あるのですが、一番、分かりやすく使い勝手がよかったのがこれでした。

react-oidcでサンプルプログラムを作ってみる

事前準備

ReactでOpenID Connectを実装する~その3で作成したサンプルプログラム(authsample1)をコピーして、react-oidcを使ったサンプルプログラム(authsample2)を作ります。

(authsample1があるディレクトリに移動しておきます)

$ cp -r authsample1 authsample2

$ cd authsample2

 

oidc-client-jsをインストールします。

$ npm install --save oidc-client
$ npm install --save babel-polyfill

react-oidcのreact-oidc-contextをインストールします。

$ npm install --save @axa-fr/react-oidc-context
$ npm install --save @axa-fr/react-oidc-context-fetch
$ npm install --save react-app-rewired

トップ画面(index.js)

トップ画面にあたるindex.jsを次のように修正します。

import React from 'react';

import ReactDOM from 'react-dom';

import { AuthenticationProvider, oidcLog, InMemoryWebStorage } from '@axa-fr/react-oidc-context';

import './index.css';

import App from './App';

import reportWebVitals from './reportWebVitals';

 

// 認証サーバ(Auth0)に接続するためのパラメータ

const configuration = {

  authority: 'https://dev-********.us.auth0.com',

  client_id: 'SJvt23j8431D********************',

  redirect_uri: 'http://localhost:3000/authentication/callback',

  extraQueryParams: {audience: 'https://dev-********.us.auth0.com/api/v2/'},

  post_logout_redirect_uri: 'http://localhost:3000/',

  silent_redirect_uri: 'http://localhost:3000/authentication/silent_callback',

  response_type: 'code',

  scope: 'openid profile email api',

  automaticSilentRenew: true,

  loadUserInfo: true,

};

 

// Auth0でのログインが成功し自サーバにリダイレクトされた時の表示処理

const CustomCallback = () => (

  <div>

    <span>認証処理中...</span>

  </div>

);

 

ReactDOM.render(

  <React.StrictMode>

    <AuthenticationProvider

      configuration={configuration}

      loggerLevel={oidcLog.DEBUG}

      isEnabled={true}

      callbackComponentOverride={CustomCallback}

      UserStore={InMemoryWebStorage}

    >

    <App />

    </AuthenticationProvider>

  </React.StrictMode>,

  document.getElementById('root')

);

reportWebVitals();

import { AuthenticationProvider, oidcLog, InMemoryWebStorage } from '@axa-fr/react-oidc-context';

先ほどインストールしたreact-oidcが提供するライブラリ(@axa-fr/react-oidc-context)を読み込みます。

const configuration = { ... }

Auth0に接続するための定義情報を設定します。

authority: 

Auth0の設定情報で記載されていたDomainを設定します。

client_id:

Auth0の設定情報で記載されていたClient IDを設定します。

redirect_uri:

Auth0の設定情報で設定したAllowed Callback URLsを設定します。

extraQueryParams: {audience: ... }

Auth0のAPI設定で記載されていたIdentifierのURL文字列を設定します。react-oidcではaudienceパラメタは標準的に準備されていないので、拡張パラメタに含めて受け渡します。

response_type: 

'code'を指定します。

scope: 

'openid profile email api'を指定します。

ちなみにresponse_typeとscopeに設定する値の意味を詳しく知りたい方は「OpenID Connect 全フロー解説」に丁寧な解説が書かれていますのでご参考に。

<AuthenticationProvider>~</AuthenticationProvider>

react-oidcが提供するタグです。メイン部である<App />を囲むように記述しているので、<App />全体がログイン認証の制御範囲になります。

callbackComponentOverride={CustomCallback}

Auth0でのログインが成功し自サーバにリダイレクトされた時の表示内容を記載します。ここでは単に「認証処理中...」と表示しています。

 

前回同様に各値がAuth0管理画面で表示されている値が異なっているとうまく接続できないため慎重に入力しましょう。

メイン部 (main.js)

メイン部分にあたるmain.jsを次のように修正します。

import React from 'react';

import { useReactOidc } from '@axa-fr/react-oidc-context';

import AppHeader from './AppHeader';

import AppContents from './AppContents';

import './App.css';

 

function App() {

  // react-oidcが提供するHooksプロパティ

  const { 

    isEnabled, // used in provider to enable/disabled.

    oidcUser,  // user information (null if not authenticated)

    login,     // login function

    logout,    // logout function

    signinSilent,

    events

  } = useReactOidc();

 

  // Auth0で正常に認証できた場合 (→ログイン後の画面を表示する)

  if (oidcUser){

    // 画面表示処理

    return (

      <div className="App">

        <div className="AppHeader">

          <AppHeader logoutFunc={logout}/>

        </div>

        <div className="AppContents">

          <AppContents user={oidcUser}/>

        </div>

      </div>

    );

  // 認証前の画面表示 (→ログインボタンを表示する)

  } else {

    return (

      <div className="App">

        <div className="AppHeader">

          <AppHeader logoutFunc="" />

        </div>

        <div className="AppContents">

          <button onClick={login}>ログイン</button>

        </div>

      </div>

    );

  }

}

export default App;

 

import { useReactOidc } from '@axa-fr/react-oidc-context';

react-oidcが提供するライブラリを読み込みます。

const { ... } = useReactOidc();

react-oidcが提供する認証用の定義情報です。とりあえずこのままコピペしましょう。

if (oidcUser) ...

oidcUserに値が格納されている場合、Auth0で正常にログイン認証ができた状態です。ログイン後に表示するメイン画面を出力します。

今回はヘッダ部<AppHeader>にlogoutメソッドを渡し、コンテンツ部<AppContents>にuser情報一式を渡して、各タグに表示処理を移譲しています。

if (oidcUser) ... else ...

oidcUserに値がない場合、まだログイン認証ができていない状態ですので、画面上にログインボタンを表示します。

ログインボタン押下時(onClick)は、react-oidcが提供するloginメソッドを呼び出して、Auth0ログイン画面を表示するよう要求します。

 

ヘッダ部 (AppHeader.js)

ヘッダ部にあたるAppHeader.jsを次のように修正します。

import React from 'react';

import './AppHeader.css';

 

// ヘッダ表示領域

// props

//   logoutFunc: useReactOidc()で取得したlogout関数

function AppHeader(props) {

  // ログアウトボタン押下時

  const handleLogout = (event) => {

    // 親コンポーネント(App)から受け渡されたlogout関数を呼び出し

    props.logoutFunc();

  }

  

  // ログアウト関数が指定されている場合 (→ログアウトボタンを表示)

  if (props.logoutFunc){

    return (

      <div className="AppHeader">

        <span></span>

        <span>OpenID Connect Sample 2</span>

        <span className='right'>

          <button onClick={handleLogout}>Logout</button>

        </span>

      </div>

    );

  // ログアウト関数が指定されていない場合 (→ログアウトボタンは非表示)

  } else {

    return (

      <div className="AppHeader">

        <span></span>

        <span>OpenID Connect Sample 2</span>

      </div>

    );

  }

}

export default AppHeader;

const handleLogout = (event) => ...

Logoutボタン押下時に実行されるメソッド。メイン部App.jsから受け渡されたlogout関数を呼び出します。

コンテンツ部 (AppContents.js)

コンテンツ部にあたるAppContents.jsを次のように修正します。

import React from 'react';

import './AppContents.css';

 

// メイン表示領域

// props

//   user: useAuth0()で取得したuser情報

function AppContents(props) {

  let oidcUser = props.user;

  if (oidcUser){

    // 画面表示処理

    return (

      <div className="AppContents">

        <div>メイン表示領域</div>

        <div>

          <span>ユーザー名:</span><span>{oidcUser.profile.name}</span>

        </div>

        <div align="left">

          {JSON.stringify(oidcUser, null, 2)}

        </div>

      </div>

    );

  } else {

    // 画面表示処理

    return (

      <div className="AppContents">

        <div>メイン表示領域</div>

      </div>

    );

  }

}

export default AppContents;

if (oidcUser) ...

メイン部App.jsから受け渡されたreact-oidcのユーザ情報props.userに値が格納されていたら表示処理を行います。

{oidcUser.profile.name}

react-oidcから受け取ったユーザ情報の中からユーザ名(ログインID)を表示します。

{JSON.stringify(oidcUser, null, 2)}

react-oidcから受け取ったユーザ情報をまるっと全部表示します。デバッグ用です。

 

動作確認

それでは実際に動かしてみます。authsample2ディレクトリ上で、Node.jsを起動します。

    (authsample2ディレクトリ内で)

    $ npm start

 

自動的にブラウザが起動します。もし起動しない場合には、http://localhost:3000/ にアクセスします。

最初はログイン認証できていない状態ですので、メイン部<App>でログインボタンを表示します。

 

ログインボタンを押下します。

すると、Auth0サイトにリダイレクトし、Auth0サイト上でログイン画面を表示します。

 

メールアドレスとパスワードには、ReactでOpenID Connectを実装する~その2で登録したユーザIDとパスワードを入力します。

正しいユーザIDとパスワードを入力すると、再び自サイト(localhost)にリダイレクトされます。

 

認証処理中の裏では、自サイトからAuth0にトークンリクエストを投げてアクセストークンを取得しています。数秒待つと、認証後の画面を表示します。

 

Logoutボタンを押下すると、ログアウト処理が行われ、再び最初のログイン画面に戻ります。

 

次回は認証プロバイダとしてAuth0を使う代わりに、自分自身で独自の認証プロバイダを立ち上げてみます。これで外部サイトに頼らずともログイン認証を実装できます。

 

ReactでOpenID Connectを実装する~その5 自前認証プロバイダを立ち上げてみる(処理フロー編)