SPECIALIST

多様な専門性を持つNRIデジタル社員のコラム、インタビューやインサイトをご紹介します。

BACK

AWS Cognito と Amplify を用いて React サイト上に OpenID Connect に準拠した SSO 機能を実現する

本記事の目的

こんにちは、倉澤です。
前回の投稿(React で紐解くモダンフロントエンド開発の歴史と進歩)に続き、今回は React サイト上に OpenID Connect に準拠した SSO(シングルサインオン)機能を実現するために必要な情報を解説していきます。AWS 上で Cognito を利用して OpenID 提供プロバイダに接続する構成を取る際に注意すべき事項も合わせて解説します。

システム構成

  • OpenID 提供プロバイダを Identify Provider(IdP)とし、AWS Cognito を Relying Party(RP)として接続する
  • AWS Amplify を用いて、React サイト上に OpenID Connect に準拠した SSO 機能を実現する

Tech Blog 倉澤 SSO 01

本システム構成の特徴
  • OpenID 提供プロバイダ(OIDC IdP)を必要に応じて追加・変更可能になる
  • 複雑な認証認可の仕様を意識すること無く React での実装が可能になる

本記事の構成

本記事は前後編の二部構成になっています。
  • 前編: OpenID Connect, Cognito を中心に認証認可のことを書きます。
  • 後編: Amplify Auth ライブラリ, React サイトでの使い方を書きます。
前編目次
  • セッションベースの認証方式とトークンベースの認証方式はどう違うのか
  • OpenID Connect / JWT とは
  • Cognito が提供する OpenID ログイン機能とは
後編目次
  • Amplify Auth ライブラリとは
  • Amplify を使うと複雑な認証認可の仕様を意識せず実装できる
  • React サイト上に OpenID Connect に準拠した SSO 機能を導入する

セッションベースの認証方式とトークンベースの認証方式はどう違うのか

サービスやデータを繋いで新たな価値を創出するビジネスが増えていく中で、本人の承認・同意を元にしてサイトごとにバラバラに登録していたユーザー情報を一元管理、連携(ID連携)する技術に注目が集まりました。

従来のように自社の「クライアント + サーバー」で構成するシステムではセッションベースの認証方式が一般的でした。サーバーサイドのデータベースで管理している「セッションIDとユーザ情報」で誰が認証されているかを判定します。Cookie を介してクライアントサイドとサーバーサイドがセッションIDをやりとりしていました。しかし、第三者である OpenID 提供プロバイダと自社のサーバーとでは従来のように共通のセッションを貼ることはできません。

そこでトークンを用います。トークンベースの認証とは、ユーザーが自分の情報を確認(ログイン)し、代わりに認証トークンを受け取る仕組みです。認証トークンが有効である限り、ユーザーはアクセスを保持します。ユーザーがログアウトするか、有効期限が切れると認証トークンは無効になります。OpenID 提供プロバイダが発行するトークンは公開鍵暗号方式を用いているため、トークンの正当性を公開鍵で検証することができます。

この点がセッションベースの認証方式よりもトークンベースの認証方式が優れているために多くの企業で活用されています。(例:Google や Facebook など)

OpenID Connect / JWT とは

OpenID Connect と JWT(JSON Web Token)はトークンベース認証の代表的なアイデンティティ連携プロトコルです。OpenID 提供プロバイダは「ID トークン、アクセストークン、リフレッシュトークン」を発行します。ID トークンとアクセストークンは認証や付帯情報取得で利用するトークンです。これらはセキュリティ向上のため、一般的に比較的短時間で失効します。失効すると新しいトークンを発行する必要がありますが、都度ログインをし直すのは非常に不便なので、一度ログインしたらトークンを再発行するための専用のトークンが一緒に払い出されます。これがリフレッシュトークンです。リフレッシュトークンは比較的長期間にわたり利用できることが多いです。

NRI セキュアテクノロジーズ株式会社の OpenID Connect の用語解説ページ を見ていただくと理解しやすいかと思います。

OpenID Connectとは、サービス間で、利用者の同意に基づきID情報を流通するための標準仕様です。利用者がOpenID提供サイトに登録したID情報を使って、ほかのOpenID対応サイトにログインすることが可能になります。氏名、住所、カード番号といった属性情報を、本人の承認のもとにサイト間で連携することにより、利用者はこれまでサイトごとにバラバラに登録していたID情報を、一元管理できるようになります。
(出典)OpenID Connect

 
Tech Blog 倉澤 SSO 02
 
OpenID Connect では Relying Party(RP)の種別に応じて3種類のフローをサポートしていますが、一般的には認可コードフロー(Authorization Code Grant)が利用されています。SPA サイトはブラウザ上で JavaScript を用いてインプリシットフローを実装することも可能ですが、アクセストークン・IDトークンを露出する可能性があるため、セキュリティの観点からも認可コードフロー(Authorization Code Grant)をご利用頂くのが良いかと思います。

Tech Blog 倉澤 SSO 03

認可コードフローではクライアントに認可コードを返却し、 クライアントはそれを直接 IDトークンおよびアクセストークンと交換を行う。これにより、 User AgentやUser Agent上の他の不正アプリケーションなどにトークンも露呈することを防ぐことができる。
(出典:認証認可の調査研究 NRIセキュアテクノロジーズ株式会社
(凡例:RP は Relying Party 、OP は OpenID Provider を表す)

 
ID トークンは署名付きの JSON Web Token(JWT)です。{ヘッダ}.{ペイロード}.{署名} の3つのセクションからなる JWS (JSON Web Signature)形式の文字列です。SPA サイトではこの JWT をブラウザに保存して SSO に利用します。

ヘッダー:このスペースには、トークンのタイプと関与する署名アルゴリズムを定義します。
ペイロード:このセクションには、トークン発行者、トークンの有効期限などを定義します。
署名:安全な署名によって転送中にメッセージが変更されなかったことを確認します。
(参考)JWT.IO

 
Tech Blog 倉澤 SSO 04

Relying Party(RP)は ID トークンを受け取った後、攻撃されることを防ぐためにペイロードと署名の検証を行います。ID トークンのヘッダとペイレードはそれぞれ base64url でエンコードされているため base64url でデコードすることで元のデータを得ます。そのデータを用いて以下の検証項目を確認するプログラムを実装してください。
 

Tech Blog 倉澤 SSO 05
(出典:認証認可の調査研究 NRIセキュアテクノロジーズ株式会社)

OpenID Connect の詳細に関しては株式会社 Authlete の川崎貴彦さんの「一番分かりやすい OpenID Connect の説明」の記事と OpenID ファウンデーション・ジャパンが翻訳している「公式の原文」や NRI セキュアテクノロジーズ株式会社の「認証認可の調査研究」はお時間がある時に是非ご覧ください。

Cognito が提供する OpenID ログイン機能とは

Cognito に関する説明は AWS の辻義一さんの「AWS Black Belt Amazon Cognito」の資料が大変わかりやすいので是非ご一読ください。Cognito ユーザープールに登録した ID/PW でログインするのが一般的な使い方ですが、外部IDプロバイダでのログインに基づいて Cognito ユーザープールにログインすることも出来ます。

対応している外部IDプロバイダは以下です。(2023/01/25時点)
  • Facebook
  • Google
  • Login with Amazon
  • Sign In with Apple
  • SAML
  • OpenID Connect (OIDC)

今回は React で OpenID 対応サイトを構築することが目的なので話は逸れてしまいますが、 Uni-ID Libra, Auth0, Keycloak 等を利用することで、Facebook や Google のように自社で OpenID 提供プロバイダ(OIDC IdP)を構築することができます。

Cognito の OpenID ログイン機能を使い OpenID 提供プロバイダ(OIDC IdP)と連携させます。設定方法は公式ドキュメントをご確認ください。

ここまでで前編の主題である「OpenID 提供プロバイダを Identify Provider(IdP)とし、AWS Cognito を Relying Party(RP)として接続する」ことの説明になります。複雑な認証認可の仕様を Cognito が吸収してくれました。ありがたいですね。

Amplify Auth ライブラリとは

後編ではこの Cognito に対して Amplify を経由して OpenID 提供プロバイダ(OIDC IdP)へのログイン機能を構築していきます。
Amplify は AWS が提供するフルスタックなフロントエンドのためのソリューションです。Firebase 等と並べて見られることが多いです。

AWS Amplify は、フロントエンドのウェブ/モバイルデベロッパーが AWS でフルスタックアプリケーションを簡単に構築、出荷、ホストできるようにする完全なソリューションであり、ユースケースの進化に合わせて幅広い AWS サービスを活用できる柔軟性を備えています。クラウドの専門知識は不要。
(出典)https://aws.amazon.com/jp/amplify/
 
領域は大きく5つに分かれています。
  • Amplify Studio (ビジュアルインターフェイス)
  • Amplify CLI (コマンドラインインターフェイス)
  • Amplify Libraries (オープンソースクライアントライブラリ)
  • Amplify UI Components (オープンソースデザインシステム)
  • Amplify Web Hosting (マネージド CI/CD とホスティング)

今回は Amplify の中でも Amplify Libraries に含まれる Auth ライブラリに限定して解説したいと思います。
Auth ライブラリはかつての amazon-cognito-identity-js をラッパーしたもので、今では統合されています。要するに Amplify Auth は Cognito を扱うための JavaScript ライブラリです。詳しくは公式ドキュメントAuthentication をご覧ください。

Amplify を使うと複雑な認証認可の仕様を意識せず実装できる

OpenID Connect の仕様を理解するまでは認証認可の仕組みは複雑に感じられると思います。その仕様を吸収してくれているのが Amplify Auth ライブラリです。その中でも代表的な関数をご紹介します。以下で紹介する4つを利用しておけば OpenID Connect に準拠した SSO 機能のユースケースでは十分でしょう。

TechBlog_kurasawa_SSO_06
(出典)AuthClass | amplify-js

ログイン処理


Auth.federatedSignIn({ customProvider: 'CustomProviderName'})

カスタムプロバイダで設定した OpenID 提供プロバイダ(OIDC IdP)にログインします。

(参考)Cognito で対応している外部IDプロバイダにログインする


Auth.federatedSignIn({ provider: 'google'})

対応している Google や Facebook 等を指定してください。

ログインセッション情報を取得する


Auth.currentSession()

現在のセッション情報を取得します。
セッションには jwt (idToken, accessToken, refreshToken)が含まれています。このメソッドはトークンの有効期限が切れている時に有効な refreshToken が提示された場合に、accessToken と idToken を自動的に更新します。つまり、このメソッドを使用すると以下の通りセッションを更新できます。

idToken, accessToken refreshToken 有効期限更新の動作
有効期限内 有効期限内なので何もしない
有効期限切れ 有効期限内 自動的に期限を更新
有効期限切れ ログインし直し
(出典)https://docs.amplify.aws/lib/auth/manageusers/q/platform/js/#retrieve-current-session

ログインセッションを判定する
現在のセッション情報(CognitoUserSession)には isValid() があります。この関数を用いてセッションが有効か否かを判定することができます。

ログイン中のユーザー情報を取得する


Auth.currentAuthenticatedUser()

現在ログインしているユーザー情報を取得します。
ユーザー情報(CognitoUser)には usernamesignInUserSession > idToken > payload に詳細情報が格納されています。

ログアウト処理


Auth.signOut()

ログアウトします。

Amplify Auth ライブラリを利用していないと、ログイン時の認可コードフロー(Authorization Code Grant)の実装や、ログイン判定(①トークンを紐解き、②ペイロードと署名の検証を行い、③アクセストークンの有効期限を判定し、④期限切れであればリフレッシュトークンを用いて有効期限の延長を行う)処理をご自身で実装する必要があります。
ここでも複雑な認証認可の仕様を Amplify Auth ライブラリが吸収してくれています。ありがたいですね。

React サイト上に OpenID Connect に準拠した SSO 機能を導入する

React サイト上に UI パーツを実装します。上記の Amplify Auth の関数を呼び出すログインボタン等のコンポーネントを作って下さい。デザインや機能に制約が無ければ Amplify UI のテンプレートを利用することをおすすめします。

Authenticator を利用すると以下のように React で OpenID Connect を利用した SSO 機能を実装することが出来ます。 ただし、前述のカスタムプロバイダで設定した OpenID 提供プロバイダ(OIDC IdP)へのログインには対応していませんので Authenticator コンポーネントをカスタマイズしてください。また、Authenticator は多言語対応をしており日本語にも出来ますが完全なものではありませんのでご利用の際には必要に応じてカスタマイズしてください。


export default function App() {
  return (
    <Authenticator socialProviders={['amazon', 'apple', 'facebook', 'google']}>
      {({ signOut, user }) => (
        <main>
          <h1>Hello {user.username}</h1>
          <button onClick={signOut}>Sign out</button>
        </main>
      )}
    </Authenticator>
  );
}
TechBlog_kurasawa_SSO_07
(出典)Configuration | Amplify UI for React

状態管理は Redux 等を用いても良いですし、気軽に作りたい時は認証イベントの変更を検知する Amplify Hub を利用すると簡単です。

Amplifyには、ハブと呼ばれるローカルイベントシステムがあります。これは、Publisher-Subscriberパターンの軽量実装であり、アプリ内のモジュールとコンポーネント間でデータを共有するために使用されます。 Amplifyは、さまざまなカテゴリのHubを使用して、ユーザーのサインインやファイルのダウンロードの通知などの認証イベントなどの特定のイベントが発生したときに相互に通信します。

 


Hub.listen('auth', ({ payload: { event, data } }) => {
  switch (event) {
    case 'signIn':
      console.log('ログイン成功');
      break;
    case 'signOut':
      console.log('ログアウト成功');
      break;
    case 'signIn_failure':
      console.log('ログイン失敗', data);
      break;
    default:
      console.log(`異常時イベント ${event}.`);
  }
});
(出典)https://docs.amplify.aws/lib/utilities/hub/q/platform/js/
ここまでが後半の「AWS Amplify を用いて、React サイト上に OpenID Connect に準拠した SSO 機能を実現する」ための説明となります。

終わりに

以上で「AWS Cognito と Amplify を用いて React サイト上に OpenID Connect に準拠した SSO 機能を実現する」ことの解説を終わります。本構成を取っていただくことで以下の2点のメリットを享受できるかと思います。是非ご検討ください。

  • OpenID 提供サイト(OIDC IdP)を必要に応じて追加・変更可能になる
  • 複雑な認証認可の仕様を意識すること無く React での実装が可能になる

TechBlog_kurasawa_SSO_08

本記事に関するお問い合わせ

NRIデジタル株式会社 倉澤
E-mail:marketing-analytics-team@nri-digital.jp