🔑

SAMLkitを使ってSAMLの認証フローテストをやってみる

2023/05/26に公開2

はじめに

Security Assertion Markup Language(SAML)を実装することになったけど、実装したコードで認証が通るかどうやってテストしたらいいんだっけ or 何にどういう設定しないといけないんだっけとなったときの資料としてSAML認証フローテストの環境準備方法をまとめてみました。

前提

今回はサービスプロバイダ(SP)側の認証基盤としてGoogle Identity Platform、IDプロバイダ(IdP)側として無料で利用できるSAMLkitを使いました。

準備

簡単なログインページをVue.js(Vue3)で実装しました。ほぼGoogle Identity Platformの公式ドキュメントどおりです。
firebaseのAPIキーとドメインの取得方法は公式ドキュメントを参照ください。

LoginPage.vue
<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の編集)でつまづいたときはこの本が全て解消してくれました!
https://zenn.dev/kxn4t/books/3778cace88911a

レスキューナウテックブログ

Discussion

ReinierReinier

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を入力する必要があります。

hosoyhosoy

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の編集が必要ないことを確認したら、近いうちに記事を更新します。