SAMLkitを使ってSAMLの認証フローテストをやってみる
はじめに
Security Assertion Markup Language(SAML)を実装することになったけど、実装したコードで認証が通るかどうやってテストしたらいいんだっけ or 何にどういう設定しないといけないんだっけとなったときの資料としてSAML認証フローテストの環境準備方法をまとめてみました。
前提
今回はサービスプロバイダ(SP)側の認証基盤としてGoogle Identity Platform、IDプロバイダ(IdP)側として無料で利用できるSAMLkitを使いました。
準備
簡単なログインページをVue.js(Vue3)で実装しました。ほぼGoogle Identity Platformの公式ドキュメントどおりです。
firebaseのAPIキーとドメインの取得方法は公式ドキュメントを参照ください。
<template>
<div>Identity Platform Quickstart</div>
<div id="message">
Loading...
</div>
<button @click="onClickSignIn">
sign in
</button>
</template>
<script setup lang="ts">
import {initializeApp} from 'firebase/app';
import {getAuth, onAuthStateChanged, signInWithEmailAndPassword, SAMLAuthProvider, signInWithPopup} from 'firebase/auth'
const provider = new SAMLAuthProvider('saml.プロバイダー名');
const onClickSignIn = async () => {
try {
const result = await signInWithPopup(auth, provider)
console.log('result', result)
} catch (e) {
console.error(e)
}
}
const config = {
apiKey: "firebaseのAPIキー",
authDomain: "firebaseのドメイン",
};
const app = initializeApp(config);
const auth = getAuth(app);
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
</style>
サインインボタンがあるだけのシンプルなログインページです
テスト環境の準備
1. SAMLkitのエンティティID、SSO URL、証明書を取得する
1. SAMLkitにアクセスし、メニューバーからIdentity providerを選択
2. SAMLkit Idp(ad hoc)を選択
3. エンティティID、SSO URL、証明書を確認
2. Identity PlatformにSAMLkitの情報を登録する
1. Identity Platformの`プロバイダを追加`から新規作成
2. 項目を埋めていく
- 名前:好きな名前をつけてOK。
saml.名前
がプロバイダIDとなる。 - エンティティID:SAMLkitのエンティティIDを入力
- SSOのURL:SAMLkitのSSO URLを入力
- 証明書1:SAMLkitの証明書を入力
- サービスプロバイダのエンティティID:サービスのURLなどを入力
コードの中でプロバイダを指定する
LoginPage.vueでプロバイダIDを指定します。
const provider = new SAMLAuthProvider('saml.samlkit-test');
ログインページからログインしてみる
ログインページのsign inボタンをクリックするとポップアップが開き、Identity Platformの設定が正しければSAMLkitにリダイレクトされます。
何も入力せずにRESPONDボタンを押すとコンソールにFirebaseErrorが表示されます。
RESPONDボタン
IdP-initiatedの場合はInResponseTo
が存在しないこと、といったニュアンスのエラーが出ているようです。
SAMLkitのレスポンスXMLを確認すると、たしかにInResponseTo
が存在していたので削除します。
SAMLkitのレスポンスXMLの確認方法
ログインボタンのリダイレクト先でQuick Responseの下の +ADD ATTRIBUTESを選択
EDIT XMLを選択
response XMLが確認できます
SAMLkitのレスポンスXMLからInResponseToを削除
レスポンスXMLにInResponseTo
が2箇所あるので削除します。
XML編集画面の下の方にあるSEND RESPONSEボタンをクリックし、Identity Platformに応答を返します。XML編集画面からページを戻ってしまうとXMLの編集内容がクリアされてしまうので注意です!
SEND RESPONSEボタン
ブラウザのウィンドウサイズが小さいと表示されないかもしれないです
新たなエラーが発生していました。NameIDが欠けているか、空だと言われています。
Identity Platformの公式ドキュメントを確認すると <saml:Subject>
と <saml:NameID>
が必要とあります。
プロバイダで必須の要素
Identity Platform は、プロバイダからのレスポンスに saml:Subject 要素と saml:NameID 要素を想定しています。プロバイダを構成するときに、これらの要素の値を定義しない場合、SAML アサーションは失敗します。
SAMLkitのresponse XMLを確認してみると、 確かに<saml:Subject>
はありましたが<saml:NameID>
には値が入っていない状態でした。
SAMLkitからのレスポンスXMLにNameIDを追加する
ログイン画面のsign inボタンを押したあとのリダイレクト先でNameIDとNameID Formatを入力します。NameIDについての説明は割愛させていただきます。
今回はNameIDに samltest
と入力し、NameID Formatに urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
を選択しました。
InResponse To
を削除したし<saml:NameID>
に値が入ったので、これでOK!とRESPOND
してみますが、相変わらずコンソールにはFirebaseErrorが表示されています。
<Conditions>
が必要だと言われているようです。
SAMLkitからのレスポンスXMLにConditions要素を追加する
Googleの公式ドキュメントには記載が見当たりませんでしたが、SAMLからのレスポンスには<Conditions>
が必要だったようです。
調べた結果、<Conditions>
には NotBefore
, NotOnOrAfter
, AudiensRescriction
の3項目が含まれること、AudiensRescriction
は必須項目であることが分かったので、SAMLkitのレスポンスXMLを編集していきます。
Conditions要素の追加
32行目あたり</saml:Subject>
の下に<saml:Conditions>
を追加します
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions
NotBefore="2014-07-17T01:01:18Z"
NotOnOrAfter="2024-01-18T06:21:48Z"
>
<saml:AudienceRestriction>
<saml:Audience>http://localhost:3000/login</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2023-05-23T10:32:18.225Z">
- NotBefore、NotOnOrAfter:アサーションの有効期限。現在時刻がこの期間内に入っている必要があります
- Audience:サービスプロバイダのエンティティID
最終的にできあがったXMLがこちら。
<samlp:Response
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
ID="SAMLkit_a8bd747fce696a447dadc6595e35df43655d7db9"
Version="2.0"
IssueInstant="2023-05-23T10:32:18.225Z"
Destination="https://hogehoge.firebaseapp.com/__/auth/handler"
>
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://samlkit.com/saml2/idp/adhoc</saml:Issuer>
<samlp:Status>
<samlp:StatusCode
Value="urn:oasis:names:tc:SAML:2.0:status:Success"
/>
</samlp:Status>
<saml:Assertion
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="SAMLkit_bcccfeaa5c10f49d81e4edf3bde77ac1ae4f2ed1"
Version="2.0"
IssueInstant="2023-05-23T10:32:18.225Z"
>
<saml:Issuer>https://samlkit.com/saml2/idp/adhoc</saml:Issuer>
<saml:Subject>
<saml:NameID
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
>samlkit</saml:NameID>
<saml:SubjectConfirmation
Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"
>
<saml:SubjectConfirmationData
NotOnOrAfter="2023-05-23T10:37:18.225Z"
Recipient="https://hogehoge.firebaseapp.com/__/auth/handler"
/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions
NotBefore="2014-07-17T01:01:18Z"
NotOnOrAfter="2024-01-18T06:21:48Z"
>
<saml:AudienceRestriction>
<saml:Audience>http://localhost:3000/login</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2023-05-23T10:32:18.225Z">
<saml:AuthnContext>
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
<saml:AttributeStatement>
<saml:Attribute Name="email">
<saml:AttributeValue
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="xs:string"
>alice@example.com</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
</samlp:Response>
コンソールを確認してみる
SAMLkitのレスポンスXMLが適切に編集されていれば、console.logで出力したIdentityPlatformのresultから認証情報が確認できます。
まとめ
IdPとして無料で使えるツールをいくつか試してみた中ではSAMLkitが使いやすいと感じました。とはいえ、SAML実装初心者にとってはつまづきポイントもありました(特にレスポンスXMLの編集)。
SAML認証フローテストの別の方法として、有料ですがGoogleワークスペースも手軽に試すことができました。Googleのサービス同士だったからかもしれないですが、GoogleワークスペースはレスポンスXMLの編集が必要なく、SAMLkitであれこれ苦戦したあとだとかなり簡単にテスト環境を準備できました。
SAMLkitでうまく行かない場合はGoogleワークスペースを試してみるのもいいかもしれないです。
参考
こちらを参考にSAML実装をすすめることができました。SAMLの仕組みを始め、SAMLkitの設定(特にレスポンスXMLの編集)でつまづいたときはこの本が全て解消してくれました!
Discussion
Hello! Great to see someone making use of SAMLkit! I see you encountered some unexpected challenges while integrating with your Vue app and Google Identity Platform. I've since fixed the issues with InResponseTo and Conditions you experienced. The NameID must still be filled out to form a valid response.
こんにちは!SAMLkitを活用しているのを見て嬉しいです!VueアプリとGoogle Identity Platformとの統合で予期せぬ課題に遭遇したようですね。InResponseToとConditionsに関する問題は修正しました。有効なレスポンスを形成するためには、引き続きNameIDを入力する必要があります。
Hello Reinier!
Thanks for developing such a useful tool to help us with our SAML development.
Thanks for fixing the issue with InResponseTo and Conditions, I will update the article soon once I confirm that editing InResponseTo and Conditions is not necessary.
こんにちはReinierさん!
SAML開発を手助けしてくれる有用なツールを開発してくれてありがとうございます。
InResponseToとConditionsに関する問題の修正ありがとうございます。InResponseToとConditionsの編集が必要ないことを確認したら、近いうちに記事を更新します。