Masayan tech blog.

  1. ブログ記事一覧>
  2. SAMLによるシングルサインオン | Pythonでの実装方法

SAMLによるシングルサインオン | Pythonでの実装方法

公開日

この記事を読むとできるようになること

  • pythonを用いて実際にsamlのシングルサインオンを実装するための方法を理解できる
  • samlによる通信のデバッグ方法を理解できる

実装方法

SP主導のシングルサインオンを前提とする

ライブラリのインストール

SP側で使用するライブラリとして、saml認証用のpython3-samlを使用する。なお、このライブラリはlibxml2-devやlibxmlsec1などに依存しており、macであればhomebrewなどでマシン上にインストールしておく必要があるので注意

pip install python3-saml

IdPでアプリケーション作成

IdPはoktaを利用する。アカウント作成後、管理画面にアクセスしテスト用のアプリケーションを作成しておく。(30日間の無料トライアルがある)。作成は、Create App Integrationボタンから可能

Sign-in methodはSAML 2.0を選択

Configure SAMLでSingle sign-on URLにはSP側の認証応答メッセージを受けるためのPOSTリクエストエンドポイントを、SP Entity IDにはSPを一意に特定するための名称を入力する

SPに登録するIdPの情報を取得する

サインオンタブを選択

IdPのSign on URL(認証応答メッセージをSPから送信する先のエンドポイント)とIssuer(IdPのエンティティID)、Signing Certificate(公開鍵暗号方式における公開鍵の証明書)を控える

SP側でエンドポイントを作成する

  • IdPへの認証要求メッセージ送信(リダイレクト)用 - GET
  • IdPからの認証応答メッセージ受信用 - POST

※このコードはFlaskでの基本的な例であり、実際の使用には適切なエラーハンドリングやセキュリティ対策が必要

IdPへの認証要求メッセージ送信(リダイレクト)用 - GET

sso_login()のメソッドに来たリクエストをもとに、SP側で認証応答メッセージを作成し、IdPにリダイレクトする。prepare_flask_request関数は、Flaskのリクエストオブジェクトをpython3-samlが期待する形式に変換する

IdPからの認証応答メッセージ受信用 - POST

sso_acs()のメソッドにきたリクエストは、IdPからのリクエスト。認証応答メッセージを検証し、SP側でも認証済みとする(コードサンプルなので、SP側で認証済みとする処理は割愛)。

from flask import Flask, request, redirect
from onelogin.saml2.auth import OneLogin_Saml2_Auth
from onelogin.saml2.utils import OneLogin_Saml2_Utils

app = Flask(__name__)

@app.route('/sso/login', methods=['GET', 'POST'])
def sso_login():
    req = prepare_flask_request(request)
    auth = OneLogin_Saml2_Auth(req, saml_settings)
    auth.login()
    return redirect(auth.get_redirect_url())

@app.route('/sso/acs', methods=['POST'])
def sso_acs():
    req = prepare_flask_request(request)
    auth = OneLogin_Saml2_Auth(req, saml_settings)
    auth.process_response()
    errors = auth.get_errors()
    if len(errors) == 0:
        if not auth.is_authenticated():
            return 'Not authenticated', 401
        else:
            return 'Authenticated'
    else:
        return 'Errors: ' + ', '.join(errors), 500

def prepare_flask_request(request):
    url_data = urlparse(request.url)
    return {
        'https': 'on' if request.scheme == 'https' else 'off',
        'http_host': request.host,
        'server_port': url_data.port,
        'script_name': request.path,
        'get_data': request.args.copy(),
        'post_data': request.form.copy()
    }

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=8000)

saml_settingsは以下のような辞書をOneLogin_Saml2_Settingsに指定したオブジェクトのインスタンスとなる

from onelogin.saml2.settings import OneLogin_Saml2_Settings

OneLogin_Saml2_Settings(
            {
                "strict": True,
                "debug": False,
                "sp": {
                    "entityId": "これは、SP側のエンティティIdなので、SPを一意に特定するためのドメインを含む文字列を指定する",
                    "assertionConsumerService": {
                        "url": "http://sp.com/saml/acs/", # IdPからの認証応答メッセージを受けるためのPOSTエンドポイント
                        "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
                    },
                    "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
                    "x509cert": "",
                    "privateKey": "",
                },
                "idp": {
                    "entityId": "http://idp.com/exk・・・", # 先ほど控えたIssuer(IdPのエンティティID)
                    "singleSignOnService": {
                        "url": "https://ipd.com/app/・・・/sso/saml", # 先ほど控えたIdPのSign on URL(認証応答メッセージをSPから送信する先のエンドポイント)
                        "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
                    },
                    "x509cert": "MIID・・・, # 先ほど控えたSigning Certificate(公開鍵暗号方式における公開鍵の証明書)
                },
            }
        )

余談:python3-samlの注意点

python3-samlの依存ライブラリであるlxmlについて、本記事投稿時点では、lxml==5.1.0を使用すると、プログラムがエラーメッセージなしでこの行でクラッシュする(しかもたまに成功する)というバグをはらんでいる。

lxml==4.9.3に下げることでひとまずは解決することは可能。

デバッグ方法

開発者ツールでsaml認証をデバッグできるChrome拡張機能があるので紹介しておく

画面上部にはリクエスト内容(samlの場合はSAMLと表示される、赤枠箇所)が、画面下部にはsamlでやり取りされる実際のxmlの内容を確認することが可能

まとめ

いかがでしたでしょうか。本記事では、pythonを用いて実際にsamlのシングルサインオンを実装するための方法と、Google Chromeの拡張機能を用いたデバッグ方法について紹介しました。samlを用いたシングルサインオンのナレッジはまだまだネット上に少ないので、実際にSaasなどでシングルサインオンを実装する必要が生じた際は、ぜひ参考にしてみてください。