メインコンテンツまでスキップ

SPAでのSSO認証フローを実装する

概要

このチュートリアルでは、KurocoをバックエンドAPIとして利用するフロントエンドSPA(Single Page Application)において、SSOログインフローの全体を実装する方法を解説します。

SSOによるログインをフロントエンドで利用するではKurocoの基本設定とgrant_tokenの概念を説明していますが、本チュートリアルではフロントエンド側の実装の詳細に焦点を当てます。具体的には、リダイレクトフローの処理、トークンの交換、ログイン後にユーザーの元のページへ復帰する方法について説明します。

以下のシーケンス図は、Microsoft Entra IDをIdPとして使用した場合の完全なフローを示しています。Kurocoで設定されたSSOプロバイダー(OAuth SP、SAML SP、IDaaS SP)であれば、同じパターンが適用されます。

SSO認証フローのシーケンス図

図を拡大して表示

学べること

  • SSOリダイレクトフローのエンドツーエンドの仕組み
  • SSOリダイレクトをまたいでユーザーの元のURLを保存・復元する方法
  • grant_tokenaccess_tokenに交換する方法
  • ログインを確定し、元のページに遷移する方法

前提条件

  • SSOが設定済みのKurocoプロジェクト(OAuth SP、SAML SP、またはIDaaS SP)

    • SSO設定画面の 「(API用) Grantトークン生成」 で対象APIにチェックを入れてください
  • 動的アクセストークンセキュリティのAPIに、以下のtokenエンドポイントを作成済みであること

    項目設定内容
    パスtoken
    カテゴリー認証
    モデルLogin(v1)
    オペレーションtoken
    use_refresh_tokenチェックあり
    access_token_lifespan86400(1日、秒単位)
    refresh_token_lifespan604800(7日、秒単位)
  • フロントエンドSPA(React、Vue、Nuxt、Next.jsなど)

まだKurocoでSSOを設定していない場合は、以下のチュートリアルを先にご参照ください:

フロー概要

SSOログインフローは以下のステップで構成されます:

  1. 未認証でユーザーが保護されたページにアクセス
  2. フロントエンドが現在のURLを保存し、KurocoのSSOログインエンドポイントにリダイレクト
  3. Kurocoがブラウザを外部IdP(例:Entra ID)にリダイレクト
  4. ユーザーがIdPで認証
  5. IdPが結果をKurocoに返却し、KurocoがフロントエンドのコールバックURLにgrant_token付きでリダイレクト
  6. フロントエンドがgrant_tokenaccess_tokenrefresh_tokenに交換
  7. フロントエンドがprofileエンドポイントを呼び出してログインを確定
  8. フロントエンドがユーザーを元のページに遷移

トークンの種類と役割

SSOフローでは3種類のトークンが登場します。それぞれ用途と寿命が異なります。

トークン用途取得方法寿命使用回数
grant_tokenaccess_tokenを発行するための一時トークンSSO認証成功後、リターンURLのクエリパラメータとして付与される非常に短い(即時交換が必要)1回のみ
access_tokenAPIリクエストの認証に使用。X-RCMS-API-ACCESS-TOKENヘッダーに設定するgrant_tokenまたはrefresh_tokenをtokenエンドポイントに送信して取得設定値(例:86400秒 = 1日)有効期限内は何度でも使用可能
refresh_token期限切れのaccess_tokenを再発行するためのトークンgrant_tokenをtokenエンドポイントに送信した際にaccess_tokenと同時に取得設定値(例:604800秒 = 7日)有効期限内は何度でも使用可能

トークンの流れ:

SSO認証成功
└→ grant_token(URLパラメータ)
└→ tokenエンドポイントに送信
├→ access_token(API認証に使用)
└→ refresh_token(access_token期限切れ時に再発行)
└→ tokenエンドポイントに送信
├→ 新しいaccess_token
└→ 新しいrefresh_token
備考

access_tokenrefresh_tokenはいずれもtokenエンドポイント(/rcms-api/{api_id}/token)から取得しますが、リクエストボディのパラメータが異なります:

  • 初回発行:{ "grant_token": "..." }
  • 再発行(リフレッシュ):{ "refresh_token": "..." }

使用するエンドポイント

本チュートリアルで使用するエンドポイントの一覧です。いずれもKuroco管理画面であらかじめ作成しておく必要があります。

エンドポイントメソッドパス用途認証ヘッダー
SSOログインGET管理画面のSSO設定から取得SSO認証フローを開始する不要
tokenPOST/rcms-api/{api_id}/tokengrant_tokenaccess_tokenの交換、refresh_tokenによる再発行不要
profileGET/rcms-api/{api_id}/profileログイン確認とユーザー情報の取得X-RCMS-API-ACCESS-TOKEN
logoutPOST/rcms-api/{api_id}/logoutログアウト処理X-RCMS-API-ACCESS-TOKEN
ヒント

tokenエンドポイントとprofileエンドポイントは、Kuroco管理画面のAPI設定で以下のように作成します:

tokenエンドポイント(前提条件の設定テーブルを参照)

  • カテゴリー:認証 / モデル:Login(v1) / オペレーション:token

profileエンドポイント

  • カテゴリー:認証 / モデル:Login(v1) / オペレーション:profile

logoutエンドポイント

  • カテゴリー:認証 / モデル:Login(v1) / オペレーション:logout

実装

ステップ1:未認証状態の検出

ユーザーが保護されたページにアクセスした際、有効なトークンがあるかを確認します。なければ、ログイン後にリダイレクトできるよう現在のURLを保存します。

const currentPath = window.location.pathname + window.location.search

if (!accessToken) {
sessionStorage.setItem("post_login_redirect", currentPath)
}
ヒント

リダイレクトURLの保存にはlocalStorageではなくsessionStorageを使用してください。ブラウザタブを閉じたときに自動的にクリアされるため、一時的なログイン状態の管理により適しています。

ステップ2:SSOログインへのリダイレクト

ユーザーをKurocoのSSOログインURLにリダイレクトします。このURLはKuroco管理画面のSSO設定画面から取得できます。

SSOの種類によって、ログインURLの確認場所が異なります:

SSO種別管理画面の場所URL表示項目
OAuth SP[外部システム連携] → [OAuth SP] → 編集画面ログインURL
SAML SP[外部システム連携] → [SAML SP] → 編集画面ログインSAML SP ACS URI
IDaaS SP[外部システム連携] → [IDaaS SP] → 編集画面ログインURL
// KurocoのAPIドメイン(独自ドメインを設定している場合はそのドメインを使用)
const KUROCO_API = "https://api.example.com"

// SSOログインURLはKuroco管理画面のSSO設定画面から取得します
// 例: https://{管理画面ドメイン}/direct/login/saml_login/?spid={sp_id}
const SSO_LOGIN_URL = "https://{management-domain}/direct/login/saml_login/?spid={sp_id}"

SSOログインURLへリダイレクトします。api_idパラメータを付与すると、grant_tokenを生成する対象APIを明示的に指定できます(管理画面で複数APIにチェックを入れている場合に有用です)。

// api_idを指定する場合はクエリパラメータに追加
window.location.href = `${SSO_LOGIN_URL}&api_id={api_id}`

// api_idが不要な場合(対象APIが1つのみの場合)
window.location.href = SSO_LOGIN_URL
注意

SSOログインURLはKuroco管理画面のSSO設定画面から取得してください。コールバックURL(認証後のリダイレクト先)は管理画面の 「リターンURL(成功)」 で設定します。ユーザーの実際の戻り先は、ステップ1で示したようにsessionStorageで別途管理します。

ステップ3:コールバックの処理

SSO認証が成功すると、Kurocoは管理画面で設定した「リターンURL(成功)」に grant_tokenmember_id をGETパラメータとして付与してリダイレクトします(例:https://front-end.example.com/?grant_token=*********&member_id=123)。コールバックページでこれらを取得します。

const params = new URLSearchParams(window.location.search)
const grantToken = params.get("grant_token")
const memberId = params.get("member_id")

if (!grantToken) {
// エラー処理:ログインページにリダイレクトするか、エラーを表示
throw new Error("コールバックURLにgrant_tokenが存在しません")
}

ステップ4:grant_tokenをaccess_tokenに交換

grant_tokenをKurocoのトークンエンドポイントに送信して、access_tokenrefresh_tokenを取得します。

const response = await fetch(
`${KUROCO_API}/rcms-api/{api_id}/token`,
{
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ grant_token: grantToken }),
}
)

const data = await response.json()
const { access_token, refresh_token } = data
備考

grant_tokenは1回限り有効な短命のトークンです。URLに露出するため、コールバック受信後直ちにアクセストークンに交換する必要があり、再利用はできません。

トークンエンドポイントのレスポンスは以下のような構造です。実際のトークン文字列は .value プロパティに格納されています:

{
"access_token": { "value": "アクセストークン文字列", ... },
"refresh_token": { "value": "リフレッシュトークン文字列", ... }
}

ステップ5:トークンの保存

トークンを安全に保存します。SPAではメモリ内での保存が推奨されます。トークン文字列は .value から取得します。

// メモリまたはセキュアなストレージに保存
setAccessToken(access_token.value)
setRefreshToken(refresh_token.value)

ステップ6:ログインの確定

新しいアクセストークンでprofileエンドポイントを呼び出し、ログインを確認します。

const profileResponse = await fetch(`${KUROCO_API}/rcms-api/{api_id}/profile`, {
credentials: "include",
headers: {
"X-RCMS-API-ACCESS-TOKEN": access_token.value,
},
})

const user = await profileResponse.json()

ステップ7:元のページへの遷移

sessionStorageから保存済みのURLを取得し、ユーザーを元のページに遷移させます。

const redirectPath =
sessionStorage.getItem("post_login_redirect") || "/"

sessionStorage.removeItem("post_login_redirect")

// ルーターのナビゲーションを使用(例:React Router、Vue Router)
navigate(redirectPath)

トークンリフレッシュ

access_tokenの有効期限が切れた場合は、refresh_tokenを使用して新しいトークンを取得します。リフレッシュも失敗した場合は、SSOフローを再実行します。

const refreshResponse = await fetch(
`${KUROCO_API}/rcms-api/{api_id}/token`,
{
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ refresh_token: refreshToken }),
}
)

if (!refreshResponse.ok) {
// リフレッシュ失敗 — SSOフローを再開
sessionStorage.setItem(
"post_login_redirect",
window.location.pathname + window.location.search
)
window.location.href = SSO_LOGIN_URL
return
}

const refreshData = await refreshResponse.json()
setAccessToken(refreshData.access_token.value)
setRefreshToken(refreshData.refresh_token.value)
ヒント

APIリクエストが401 Unauthorizedを返した場合に、自動的にrefresh_tokenでトークンを再取得してリクエストをリトライする仕組みを実装すると、ユーザー体験が向上します。リトライも失敗した場合はトークンを破棄してSSOフローを再開します。

重要な設計ポイント

SSOログインURLとコールバックURL

SSOログインURL(例:https://{管理画面ドメイン}/direct/login/saml_login/?spid={sp_id})と、認証後のコールバックURL(リターンURL)は、いずれもKuroco管理画面のSSO設定で管理します。SSO設定画面の 「(API用) Grantトークン生成」 で対象APIにチェックを入れることで、リターンURL遷移時にgrant_tokenパラメータが付与されるようになります。ユーザーの実際の戻り先は、フロントエンド側でsessionStorageを使用して別途管理します。

認証スコープごとに1つのAPI

Kurocoでは、動的アクセストークン認証の場合、ログインセッションはAPI(api_id)単位でスコープされます。SSOログインを使用した場合、ユーザーはSSOが設定されたAPIに対してのみ認証されます。認証が必要なすべてのエンドポイントを同一のapi_idにまとめることで、セッションの問題を回避できます。

注意: Cookie認証の場合は、複数のapi_id間で認証状態が共有されます。詳細はAPIのセキュリティを参照してください。

grant_tokenのセキュリティ

grant_tokenはURLに露出するため、コールバックで受け取ったら直ちにaccess_tokenに交換してください。詳細は上記のトークンの種類と役割を参照してください。

関連ドキュメント


サポート

お探しのページは見つかりましたか?解決しない場合は、問い合わせフォームからお問い合わせいただくか、Slackコミュニティにご参加ください。