Schoo Advent Calendar 2025 3日目の記事は、こうざい(@yukhy_BE)と、たきざわで取り組んできたことについて、共著の形でお届けします。
* * *
弊社プロダクトのSchoo for Businessには、SAMLを使用したシングルサインオン(以下、SSO)機能があります。
エンタープライズ企業様で利用されているIDプロバイダー(以下、IdP)とシステム連携し、アカウントの一元利用の手段として有効活用していただいています。
開発環境にてSSO機能の動作確認を行う際、IdPとしてAuth0の利用を検証してきました。
下記の記事のように既に試されている方もいますが、Auth0はIdPとしても非常に優秀で、手軽にSAMLレスポンスを生成できるため重宝しています。
しかし、検証を進める中で2つの課題に直面しました。
- 標準以外の属性(ユーザーメタデータ)をどうやってSAMLレスポンスに含めるか
- そのデータをどうやって手軽に用意するか
本記事では、Auth0 ActionsとAuth0 Formsを組み合わせることで、SSOを検証したい開発者が検証に必要なSAML属性の付与をスムーズに行える仕組みを構築した事例を紹介します。
前提と実現したいこと
背景とユースケース
SchooのSSO機能によるログインでは、単なる認証(ログインできるかどうか)だけでなく、SAMLレスポンスに含まれる属性値を利用して権限コントロールを行っています。
(例)
- 所属団体コード: どの組織に属しているか
- 役職/グループ: 一般社員か管理者か
これらの情報は、標準的なSAMLレスポンス(emailやnameなど)には含まれていません。そのため、Auth0の user_metadata に保存されている付加情報を、SAML属性としてマッピングし、サービスプロバイダ(この場合はSchoo)に渡す必要があります。
実装の全体像
今回構築したフローは以下の通りです。
Step 1: ユーザーメタデータの付与 (Auth0 Formsの活用)
まずは、SAMLレスポンスに記載する値をどう用意するか?という問題について考えます。
課題:手動登録は面倒
通常、特定の検証用ユーザーにメタデータを付与するには、Auth0ダッシュボードに管理者としてログインし、[User Details] > [Metadata] からJSONを手動で編集する必要があります。
// metadataの例
{
"groups": "SchooSampleGroup",
"department": {
"code": "dev_01",
"name": "Development"
}
}
しかし、SSO機能の検証を行うメンバー全員にAuth0のダッシュボード権限を渡すのはセキュリティ的に懸念がありますし、動作検証のたびに管理画面を行き来するのは手間です。
弊社では以前、Auth0以外のIdPを検証用のIdPとして利用をしていましたが、そこでもこの課題がありました。
解決策:Auth0 Formsで登録時に入力させる
そこで、Auth0 Formsを利用しました。ユーザーがアカウントを作成(または初回ログイン)する際にフォームを表示し、必要な情報を自分で入力してもらう方式です。
参考
FormsとFlowの設定
Auth0 DashboardのForms機能にて、以下のようなフォームを作成しました。
- Input: 所属グループ(
groups)などを入力するフィールドを作成 - Flow:
UpdateUserブロックを使用し、フォームの入力値をuser_metadata.groupsに保存するように設定
Actions内での呼び出し
作成したフォームをログインフローの中で表示するために、 Post Login トリガー(ログイン後)を起点として実行する処理を実装します。
本来は Post User Registration トリガー(ユーザー新規作成後)で行いたいところですが、現在 api.prompt.render(フォームの表示)が利用できるのは Post Login トリガーのみであるため、ここで実装しています。
exports.onExecutePostLogin = async (event, api) => {
// すでにmetadataがある場合はスキップするなどの分岐を入れると良い
if (!event.user.user_metadata.groups) {
// 作成したFormを表示 ('ap_xxx' はFormのID)
api.prompt.render('ap_xxxxxxxxxxxxxx');
}
};
これにより、動作検証担当者はAuth0の管理画面に入ることなく、ブラウザ上の操作だけで必要なメタデータを持ったユーザーを用意できるようになります。
Step 2: ユーザーメタデータをSAMLレスポンスに渡す
次に、 user_metadata に入った情報をSAMLレスポンスに埋め込む処理です。こちらも Post Login トリガーの関数内で完結させます。
スクリプトの実装
Forms表示処理後の追加処理として以下のコードを実装しました。
exports.onExecutePostLogin = async (event, api) => {
// 1. ユーザーメタデータ全体を取得
// (Forms経由ですでに保存されている前提)
const userMetadata = event.user.user_metadata || {};
// 2. SAML属性としてセット
// api.samlResponse.setAttribute(属性名, 値) を使用します
if (userMetadata.groups) {
api.samlResponse.setAttribute(
// 属性名として一般的なクレームURIを使用(SP側でのマッピングが容易になるため)
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/groups',
userMetadata.groups // user_metadata.groupsに格納されている文字列を渡す
);
}
};
- api.samlResponse.setAttribute: これが肝となるメソッドで、ここで任意のユーザーデータを任意のSAMLクレーム名でマッピングし、SAMLレスポンスに付与します
- 属性名(URI): 単なる groups ではなく、http://schemas.xmlsoap.org/... のような標準的なURI形式を採用することで、Microsoft系など厳格なSP実装との互換性を高めています
おまけ: SAML Tracerによる動作検証
実装が正しいかどうかの確認には、Chrome拡張機能の SAML Tracer を使用しました。
これを使うと、ブラウザとIdP(Auth0)⇆SP(Schoo)間でやり取りされるSAMLレスポンスの中身をデコードして見ることができます。
下記の記事を参考にさせてもらいました。
以下のように、<saml:AttributeStatement> の中に、指定したURIと値(例: SchooSampleGroup)が含まれていれば成功です。
<saml:AttributeStatement>
<!-- 追加されたカスタム属性 -->
<saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/groups"
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">
SchooSampleGroup
</saml:AttributeValue>
</saml:Attribute>
<!-- その他の標準属性(emailなど) -->
<saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" ...>
...
</saml:AttributeStatement>
まとめ
Auth0 ActionsとFormsを組み合わせることで、標準以外の属性を入力しSAMLレスポンスに含めるところまでを一貫したフローとして構築することができました。
以前は動作検証のたびに管理画面でJSONを編集していましたが、この仕組みを導入してからは検証用アカウントの作成がセルフサービス化され、SSO機能の検証にかかる工数を大幅に減らすことができました。
Actionsは単なるロジックの実行だけでなく、UI(Forms)と組み合わせることで活用の幅が大きく広がると実感しています。
参考文献
- Auth0でSAML SP/IdPを構築する - Qiita
- Auth0新機能のForms触ってみた - Zenn
- Actionsを使用したフォームの表示 - Auth0 Docs
- SAMLアサーションをカスタマイズする - Auth0 Docs
- 身近なSAML認証の中身を見てみた - Zenn
- Okta - 便利なChrome拡張機能 - SAML-tracer Qiita
Schooでは一緒に働く仲間を募集しています!
