Page tree
Skip to end of metadata
Go to start of metadata

This article describes how to use ForgeRock AM as an SAML2 IdP for a python Django application (SP).

This article uses the https://github.com/fangli/django-saml2-auth library to integrate AM SAML2 authentiation into a Django appication. We will use https://github.com/sandman0/2do1ist as
a sample Django application for this purpose.

Prerequisites

  1. This article assumes CentOS 7. Some steps may vary depending on the OS used.
  2. Python 3 (3.6.8 for this example) installed. To install there are many tutorials available online, on example is [here] (https://www.digitalocean.com/community/tutorials/how-to-install-python-3-and-set-up-a-local-programming-environment-on-centos-7)
  3. A working instance of AM 6.5. Should work with older and newer versions of AM too.

High level steps

  1. Deploy, run and test the sample application as-is in a python virtual environment
  2. Create SAML2 federation configuration in AM
  3. Configure the sample application, using django-saml2-auth library to use AM as an IdP

AM is deployed under http://openam.example.com:8080/openam and the django application is deployed under http://todo.example.net:8000

Detailed steps

  1. Deploy, run and test the sample application as-is in a python virtual environment


    a. Create and activate a python virtual environment

    [forgerock@centosvm ~]$ mkdir django-saml2
    [forgerock@centosvm ~]$ cd django-saml2/
    [forgerock@centosvm django-saml2]$ python3.6 -m venv django-saml2-venv
    [forgerock@centosvm django-saml2]$ source django-saml2-venv/bin/activate
    (django-saml2-venv) [forgerock@centosvm django-saml2]$

    b. Install django in the venv

    (django-saml2-venv) [forgerock@centosvm 2do1ist]$ pip3 install Django==2.1.*
    Collecting Django==2.1.*
      Using cached https://files.pythonhosted.org/packages/bb/47/a4fdf24409656dc624a802571c3d6bb809e396ebbe6d668b16cb8ae431fa/Django-2.1.9-py3-none-any.whl
    Collecting pytz (from Django==2.1.*)
      Using cached https://files.pythonhosted.org/packages/3d/73/fe30c2daaaa0713420d0382b16fbb761409f532c56bdcc514bf7b6262bb6/pytz-2019.1-py2.py3-none-any.whl
    Installing collected packages: pytz, Django
    Successfully installed Django-2.1.9 pytz-2019.1
    (django-saml2-venv) [forgerock@centosvm 2do1ist]$

    c. Clone the sample appication

    (django-saml2-venv) [forgerock@centosvm django-saml2]$ git clone https://github.com/sandman0/2do1ist.git
    Cloning into '2do1ist'...
    .
    .

    d. Create DB (sqlite) schema

    (django-saml2-venv) [forgerock@centosvm 2do1ist]$ python3.6 manage.py makemigrations
    Migrations for 'todolist':
      todolist/migrations/0001_initial.py
        - Create model Todo
    (django-saml2-venv) [forgerock@centosvm 2do1ist]$ python3.6 manage.py migrate
    Operations to perform:
      Apply all migrations: admin, auth, contenttypes, sessions, todolist
    Running migrations:
      Applying contenttypes.0001_initial... OK
      Applying auth.0001_initial... OK
      .
      .
      .
      Applying auth.0009_alter_user_last_name_max_length... OK
      Applying sessions.0001_initial... OK
      Applying todolist.0001_initial... OK
    (django-saml2-venv) [forgerock@centosvm 2do1ist]$

    e. Run the appication

    (django-saml2-venv) [forgerock@centosvm 2do1ist]$ python3.6 manage.py runserver 0.0.0.0:8000
    Performing system checks...

    System check identified no issues (0 silenced).
    June 10, 2019 - 22:55:15
    Django version 2.1.9, using settings 'mysite.settings'
    Starting development server at http://0.0.0.0:8000/
    Quit the server with CONTROL-C.

    f. Access the application in your browser. Login screen is shown

    [[ login screen ]]

    g. Create two users and login and create some sample to do items for both.
    usernames: testuser1 and testuser2
    passwords: s#cret123
  2. Create SAML2 federation configuration in AM


    a. Create a subrealm /django for this exercise (optional)

    b. Use the "Configure SAMLv2 Provider" wizard to create a hosted IdP with the following settings:

    Realm: /django
    Name: djangoidp
    Signing Key: test (only used for tetsing purpose, not at all meant for production)
    New Circle of Trust: django-cot
    Attribute Mappings:
        uid=uid
        sn=sn
        givenName=givenName
        mail=mail

    c. For ease, use the "Create a Fedlet" wizard to create a Fedlet configuration, with the following settings:

    Realm: /django
    Circle of Trust: django-cot (real-only)
    Identity Provider: djangoidp (read-only)
    Name: djangosp
    Destination URL of the Service Provider which will include the Fedlet: http://todo.example.net:8000
    Attribute Mapping:
        uid=uid
        sn=sn
        givenName=givenName
        mail=mail

    d. Go to the remote SP configuration in AM console, to the Services tab and change the HTTP-POST assertion consumer service URL to http://todo.example.net:8000/saml2_auth/acs/

    e. Fetch the IdP metadata from AM from http://openam.example.com:8080/openam/saml2/jsp/exportmetadata.jsp?entityid=djangoidp&realm=/django and save it in a file, for example: /home/forgerock/django-saml2/metadata.xml

    f. Create users testuser1 and testuser2 in the /django realm in AM. Set the mail, givenName and sn values for the users.
  3. Configure the sample application, using django-saml2-auth library to use AM as an IdP

    a. Stop the django application if running and install django-saml2-auth in the virtual environment

    (django-saml2-venv) [forgerock@centosvm django-saml2]$ pip3 install django_saml2_auth
    Collecting django_saml2_auth
      Using cached https://files.pythonhosted.org/packages/03/5e/4fda29b8be8e268cbd3aced6ca70987a9efb0a75f42b061461bd63df4d04/django_saml2_auth-2.2.1.tar.gz
    .
    .
    .
    Successfully installed .... long list of libs
    (django-saml2-venv) [forgerock@centosvm django-saml2]$

    b. Edit django-saml2/2do1ist/urls.py and un-comment the lines starting with re_path, so it reads:

        # These are the SAML2 related URLs. You can change "^saml2_auth/" regex to
        # any path you want, like "^sso_auth/", "^sso_login/", etc. (required)
        re_path(r'^saml2_auth/', include('django_saml2_auth.urls')),

        # The following line will replace the default user login with SAML2 (optional)
        # If you want to specific the after-login-redirect-URL, use parameter "?next=/the/path/you/want"
        # with this view.
        re_path(r'^accounts/login/$', django_saml2_auth.views.signin),

        # The following line will replace the admin login with SAML2 (optional)
        # If you want to specific the after-login-redirect-URL, use parameter "?next=/the/path/you/want"
        # with this view.
        re_path(r'^admin/login/$', django_saml2_auth.views.signin)

    also un-comment the following line:

    import django_saml2_auth.views

    c. Edit 2do1ist/mysite/settings.py and un-comment the SAML2_AUTH section at the very end, so it reads:

    SAML2_AUTH = {
        'METADATA_LOCAL_FILE_PATH': '/home/forgerock/django-saml2/metadata.xml',
        'DEFAULT_NEXT_URL': '/',
        'CREATE_USER': 'FALSE',
        'ATTRIBUTES_MAP': {
            'email': 'mail',
            'username': 'uid',
            'first_name': 'givenName',
            'last_name': 'sn',
        },
        'ENTITY_ID': 'djangosp',
        'USE_JWT': False,
        'FRONTEND_URL': 'https://myfrontendclient.com',
    }

    d. Launch the application

    (django-saml2-venv) [forgerock@centosvm 2do1ist]$ python3.6 manage.py runserver 0.0.0.0:8000
    Performing system checks...

    System check identified no issues (0 silenced).
    June 10, 2019 - 23:25:55
    Django version 2.1.9, using settings 'mysite.settings'
    Starting development server at http://0.0.0.0:8000/
    Quit the server with CONTROL-C.


    e. Access the application in the browser again - you should be redirected to AM login page, with the URL similar to:

    http://openam.example.com:8080/openam/XUI/?realm=/django&forward=true&spEntityID=djangosp&goto=/SSORedirect/metaAlias/django/idp?ReqID%3Did-07TOxB3yhlo6tAsVg%26index%3Dnull%26acsURL%3Dhttp://todo.example.net:8000/saml2_auth/acs/%26spEntityID%3Ddjangosp%26binding%3Durn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST&AMAuthCookie=#login/

    Login as testuser1 or testuser2 in AM. AM should send a POST to http://todo.example.net:8000/saml2_auth/acs/ with SAMLResponse in the POST payload. You should be redirected to the application main page with the todo list for the user you logged in as.

    The decoded SAML assertion will look like:

    <samlp:Response Destination="http://todo.example.net:8000/saml2_auth/acs/"
        ID="s279fe23917842cdc4f50e63249db753daab02561f" InResponseTo="id-hZnmzTaHb2zFEqpUz"
        IssueInstant="2019-06-10T23:31:00Z" Version="2.0" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
        <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">djangoidp</saml:Issuer>
        <samlp:Status xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
            <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"
                xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"></samlp:StatusCode>
        </samlp:Status>
        <saml:Assertion ID="s2d1a5ac225906d844234c47162029ca330ee07ad1" IssueInstant="2019-06-10T23:31:00Z"
            Version="2.0" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
            <saml:Issuer>djangoidp</saml:Issuer>
            <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                <ds:SignedInfo>
                    <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                    <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
                    <ds:Reference URI="#s2d1a5ac225906d844234c47162029ca330ee07ad1">
                        <ds:Transforms>
                            <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
                            <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                        </ds:Transforms>
                        <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
                        <ds:DigestValue>bBRg5ErGCCi54IuMC9hcjmAE0aY8t6nikhije+VFRJA=</ds:DigestValue>
                    </ds:Reference>
                </ds:SignedInfo>
                <ds:SignatureValue>
                    EnxhuogdTSC1YEwyjWlY9WsbpQ89ROec9jp6atnKmDQfLeeO1AgBSrbsyj+phQe05RyiEA/8Dx8Q&#13;
                    bDwjl2t1uuYAizeCp8oWrkGRB9q50Hb3bsPd3FSJUBK+LAgsYo28GAL6cwDx4wYbvnvZ6NeoiaXR&#13;
                    8tihcdDqc/rZpqQ9U4V+U8ajXSF2/ePazhJgml/U0lW+M6LiCvNMB9KFYfB9zGfQ2gNa95zcxQKm&#13;
                    ar5FCBIGvrejFRWEb1GVfAyGVzJG4js0qVZdbxeGbup3sfE5x4QY0Axeab53rUReq3aRtUtlvuUF&#13;
                    hrYb8j3tyrdKhJXs1GpaDzE/yTm+tSimNQ4kEA==
                </ds:SignatureValue>
                <ds:KeyInfo>
                    <ds:X509Data>
                        <ds:X509Certificate>
                            MIIDYTCCAkmgAwIBAgIEFt4OQjANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJVSzEQMA4GA1UE&#13;
                            CBMHQnJpc3RvbDEQMA4GA1UEBxMHQnJpc3RvbDESMBAGA1UEChMJRm9yZ2VSb2NrMQswCQYDVQQL&#13;
                            EwJBTTENMAsGA1UEAxMEdGVzdDAeFw0xODA0MDMxNDIwNThaFw0yODAzMzExNDIwNThaMGExCzAJ&#13;
                            BgNVBAYTAlVLMRAwDgYDVQQIEwdCcmlzdG9sMRAwDgYDVQQHEwdCcmlzdG9sMRIwEAYDVQQKEwlG&#13;
                            b3JnZVJvY2sxCzAJBgNVBAsTAkFNMQ0wCwYDVQQDEwR0ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOC&#13;
                            AQ8AMIIBCgKCAQEAi7t6m4d/02dZ8dOe+DFcuUYiOWueHlNkFwdUfOs06eUETOV6Y9WCXu3D71db&#13;
                            F0Fhou69ez5c3HAZrSVS2qC1Htw9NkVlLDeED7qwQQMmSr7RFYNQ6BYekAtn/ScFHpq8Tx4BzhcD&#13;
                            b6P0+PHCo+bkQedxwhbMD412KSM2UAVQaZ+TW+ngdaaVEs1Cgl4b8xxZ9ZuApXZfpddNdgvjBeeY&#13;
                            QbZnaqU3b0P5YE0s0YvIQqYmTjxh4RyLfkt6s/BS1obWUOC+0ChRWlpWE7QTEVEWJP5yt8hgZ5Me&#13;
                            cTmBi3yZ/0ts3NsL83413NdbWYh+ChtP696mZbJozflF8jR9pewTbQIDAQABoyEwHzAdBgNVHQ4E&#13;
                            FgQUDAvAglxsoXuEwI2NT1hFtVww2SUwDQYJKoZIhvcNAQELBQADggEBADiHqUwRlq1xdHP7S387&#13;
                            vMLOr+/OUgNvDUogeyrpdj5vFve/CBxSFlcoY215eE0xzj2+bQoe5To3s8CWkP9hqB3EdhaRBfCr&#13;
                            d8Vpvu8xBZcxQzmqwNjmeDrxNpKes717t05fDGgygUM8xIBs29JwRzHzf7e0ByJjn9fvlUjDAGZ7&#13;
                            emCTN382F2iOeLC2ibVl7dpmsWZTINhQRbmq5L4ztOcjITk5WZnBF439oRRn68fWZVkOv2UqaKbk&#13;
                            uMjgotNuot+ebHtOchEiwKz8VAK7O3/IgD6rfNBfz+c/WeoPcrfQBR4zfizw/ioR115RSywifzlw&#13;
                            q5yziqyU04eP4wLr3cM=
                        </ds:X509Certificate>
                    </ds:X509Data>
                </ds:KeyInfo>
            </ds:Signature>
            <saml:Subject>
                <saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient" NameQualifier="djangoidp"
                    SPNameQualifier="djangosp">rAm9eYcvPMlcTpoN3MkHJpacNCp2</saml:NameID>
                <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
                    <saml:SubjectConfirmationData InResponseTo="id-hZnmzTaHb2zFEqpUz"
                        NotOnOrAfter="2019-06-10T23:41:00Z" Recipient="http://todo.example.net:8000/saml2_auth/acs/"/></saml:SubjectConfirmation>
            </saml:Subject>
            <saml:Conditions NotBefore="2019-06-10T23:21:00Z" NotOnOrAfter="2019-06-10T23:41:00Z">
                <saml:AudienceRestriction>
                    <saml:Audience>djangosp</saml:Audience>
                </saml:AudienceRestriction>
            </saml:Conditions>
            <saml:AuthnStatement AuthnInstant="2019-06-10T23:31:00Z"
                SessionIndex="s287b483c84580740e2216166d10dd554f1a91e501">
                <saml:AuthnContext>
                    <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
                </saml:AuthnContext>
            </saml:AuthnStatement>
            <saml:AttributeStatement>
                <saml:Attribute Name="uid">
                    <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">testuser1</saml:AttributeValue>
                </saml:Attribute>
                <saml:Attribute Name="sn">
                    <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">user1</saml:AttributeValue>
                </saml:Attribute>
                <saml:Attribute Name="mail">
                    <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">testuser1@example.com</saml:AttributeValue>
                </saml:Attribute>
                <saml:Attribute Name="givenName">
                    <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">test1</saml:AttributeValue>
                </saml:Attribute>
            </saml:AttributeStatement>
        </saml:Assertion>
    </samlp:Response>