SAML対応アプリケーションを作ってAWS SSOで認証してみる

AWS SSOとは

> AWS Single Sign-On (AWS SSO) とは、AWS 上でワークフォースアイデンティティを作成、または接続し、AWS 組織全体のアクセスを一元的に管理するものです。

（公式サイトより）

SAML対応アプリケーションのサンプルを使用して、AWS SSOで認証してみました。


# SAML対応アプリケーションの準備

今回試した環境は以下のとおりです。

| 項目 | 内容 | 
| --- | --- |
| インスタンスタイプ | t4g.small	 |
| OS | Ubuntu 20.04 |
| Public IPアドレス | 3.80.194.90 |
| Public IPv4 DNS | ec2-3-80-194-90.compute-1.amazonaws.com |

SAML連携用にこちらのライブラリーを使用します。サンプルプログラムも含まれています。

* OneLogin's SAML Python Toolkit (compatible with Python3)

https://github.com/onelogin/python3-saml

* 環境準備

```console:ubuntu
sudo apt update && sudo apt install -y libxmlsec1-dev python3-pip pkg-config
```

* ソースコードの取得

```console:ubuntu
git clone https://github.com/onelogin/python3-saml.git
```

* 依存ライブラリーの取得

```
cd python3-saml/demo-django
sudo pip3 install python3-saml
sudo pip3 install -r requirements.txt
```

# AWS SSO側の設定

マネージメントコンソールでAWS SSOを開き、左メニューのアプリケーションを選択します。
「新規アプリケーションの追加」ボタンを押し、ウィザードに従って進めます。

* 新規アプリケーションの追加

「カスタム SAML 2.0 アプリケーションの追加」を選択

![console.aws.amazon.com_singlesignon_home_region=us-east-1(Surface Pro 7) (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/8e4baacb-ec4b-5e0c-47c9-b7e4e03b81d9.png)

設定を入力していきます。

![console.aws.amazon.com_singlesignon_home_region=us-east-1(Surface Pro 7) (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/bd92651c-bbe5-ed8e-bb26-10ed0fa60774.png)

* 詳細

| 項目 | 値 |
| --- | --- |
| 表示名 | Django Sample |
| 説明 | Django Sample |

* AWS SSO メタデータ

あとでサンプルアプリケーション反映する値です。
「AWS SSO SAML メタデータファイル」の横にあるダウンロードリンクからファイルを取得しておきます。

* アプリケーションのプロパティ

| 項目 | 値 |
| --- | --- |
| アプリケーション 開始 URL | http://ec2-3-80-194-90.compute-1.amazonaws.com:8000/ |
| リレーステート | http://ec2-3-80-194-90.compute-1.amazonaws.com:8000/ |
| セッション時間 | 1時間 |

* アプリケーションメタデータ

「メタデータファイルがない場合は、手動でメタデータ値を入力できます。」をクリックし、手動入力欄を表示します。

| 項目 | 値 |
| --- | --- |
| アプリケーション ACS URL | http://ec2-3-80-194-90.compute-1.amazonaws.com:8000/?acs |
| アプリケーション SAML 対象者 | http://ec2-3-80-194-90.compute-1.amazonaws.com:8000/metadata/ |

これでアプリケーションの登録が完了です。
追加で属性マッピングを行います。

![console.aws.amazon.com_singlesignon_home_region=us-east-1(Surface Pro 7) (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/9d88738d-0649-5d11-701d-2f8b0c6d9b09.png)

| アプリケーションのユーザー属性 | この文字列値またはAWS SSOのユーザー属性にマッピング | 形式 |
| --- | --- | --- |
| Subject | ${user:subject} | unspecified | 
| email | ${user:email} | unspecified | 

設定できる属性についてはこのあたりのページを参考にしました。
https://docs.aws.amazon.com/singlesignon/latest/userguide/attributemappingsconcept.html?icmpid=docs_sso_console


# SAML対応アプリケーションの設定

設定値を変更していきます。

* demo-django/saml/settings.json

```xml
{
    "strict": true,
    "debug": true,
    "sp": {
        "entityId": "http://ec2-3-80-194-90.compute-1.amazonaws.com:8000/metadata/",
        "assertionConsumerService": {
            "url": "http://ec2-3-80-194-90.compute-1.amazonaws.com:8000/?acs",
            "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
        },
        "singleLogoutService": {
            "url": "http://ec2-3-80-194-90.compute-1.amazonaws.com:8000/?sls",
            "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
        },
        "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
        "x509cert": "",
        "privateKey": ""
    },
    "idp": {
        "entityId": "[AWS SSO SAML メタデータファイルに記載の値]",
        "singleSignOnService": {
            "url": "[AWS SSO SAML メタデータファイルに記載の値]",
            "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
        },
        "singleLogoutService": {
            "url": "[AWS SSO SAML メタデータファイルに記載の値]",
            "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
        },
        "x509cert": "[AWS SSO SAML メタデータファイルに記載の値]"
    }
}

```

* demo-django/demo/settings.py

EC2のPublic IPv4 DNSを追加します。

```python
ALLOWED_HOSTS = ['ec2-3-80-194-90.compute-1.amazonaws.com']
```

設定は以上です。

# SAML対応アプリケーションの起動

```console
python3 manage.py runserver 0.0.0.0:8000
```

# 動作確認

動作を見てみます。まずは、アプリケーションのトップ画面にアクセス。

* http://ec2-3-80-194-90.compute-1.amazonaws.com:8000/

真っ白ですが、ログイン前の画面です。

![ec2-3-80-194-90.compute-1.amazonaws.com_8000_(Surface Pro 7).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/297a9719-bf9d-abb1-74b2-e834966f6dd6.png)

Loginボタンを押すと、AWS SSO側のリダイレクトされ、ログインフォームが表示されます。

![us-east-1.signin.aws_platform_login_workflowStateHandle=1dc27fb7-f50d-4b4a-b595-6d1dbeb763b2(Surface Pro 7).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/25c13c82-3ec5-852b-aa4c-1c381d6e1089.png)

ログイン完了後は、アプリケーション側の画面に遷移します。

![ec2-3-80-194-90.compute-1.amazonaws.com_8000_(Surface Pro 7) (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/8ca3891a-5c22-448c-0a20-6bc7cf40abc5.png)

属性マッピングで設定したメールアドレスが確認できると思います。

# その他

Proxy経由になる場合などは`demo-django/demo/views.py`の`prepare_django_request`メソッドを調整する必要があります。

```python:views.py
def prepare_django_request(request):
    # If server is behind proxys or balancers use the HTTP_X_FORWARDED fields
    result = {
        'https': 'on' if request.is_secure() else 'off',
        'http_host': request.META['HTTP_HOST'],
        'script_name': request.META['PATH_INFO'],
        'get_data': request.GET.copy(),
        # Uncomment if using ADFS as IdP, https://github.com/onelogin/python-saml/pull/144
        # 'lowercase_urlencoding': True,
        'post_data': request.POST.copy()
    }
    return result

```

# 参考サイト

PythonでSAMLを実装
https://qiita.com/ykensuke/items/5d7f5d332bfbafd918bd