Friday, October 7, 2016

Web SSO - part 2 - integration with LDAP

In my previous article I presented how to create a web SSO system using SimpleSAMLphp and Symfony. The users where declared directly in simpleSAMLphp using "exampleauth:UserPass".

In many companies a LDAP server is the source from where information about user authentication is taken. I will install  OpenLDAP and configure my applications to use it.

1. Install OpenLDAP and phpLDAPadmin

For installing OpenLDAP and phpLDAPadmin I followed this tutorials from DigitalOcean:

https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-openldap-and-phpldapadmin-on-an-ubuntu-14-04-server

You will need to edit also the ldap.conf file, see this thread on StackOverflow

Also you may get an error when trying to login with phpLDAPadmin  "Notice: Undefined variable: _SESSION in ..". 
For me this solution from StackOverflow solved the problem:

"Just add the user wich is running Apache2 (or php5-fpm!) to the system group "www-data" (debian) and restart services apache AND if used php5-fpm both.
Get the User apache is running as:

~# sed -rn 's/^User (.+)/\1/p' /etc/apache2/apache2.conf"

Using phpLDAPadmin I've created two groups "admin" and "regular_users" and also I've created some users allocated to these two groups.

2. Modify SimpleLDAPphp to use OpenLDAP


The documentation for using LDAP authentication is found here: https://simplesamlphp.org/docs/stable/ldap:ldap

My settings are:


Select LDAP authentication to be used from   /metadata/saml20-idp-hosted.php

 /* 
         *Authentication source to use. Must be one that is configured in
* 'config/authsources.php'.
*/
'auth' => 'example-ldap',

3. Modify Symfony app

In the current Symfony application I am expecting an attribute roles containing an array of roles. From LDAP I will receive different attributes, one of them is gidNumber, which is a number identifying a group. My current groups: admin and regular_users have gidNumber 500 and 501.
I will be using these gidNumbers to correctly create roles in the Symfony application.

The changes to be made are done in the UserCreator class:


Of course you need to change these mappings to fit your situation.

4. Test

First make sure to delete any sessions and cookies. After that try to access the secure route from consumer1.local, login with any user from LDAP and you should be redirected to secure area. Check in database if the user and user roles were created correctly.

Web SSO with Symfony and SimpleSAMLphp

1. Single sign on (SSO)


Sooner or later web development teams face one problem: you have developed an application at domain X and now you want your new application at domain Y to use the same login information as the other domain. In fact, you want more: you want users who are already logged-in at domain X to be already logged-in at domain Y. This is what SSO is all about. (source: https://auth0.com/blog/what-is-and-how-does-single-sign-on-work )

2. Concepts:


Identity Provider (IdP)  - is responsible for (a) providing identifiers for users looking to interact with a system, (b) asserting to such a system that such an identifier presented by a user is known to the provider, and (c) possibly providing other information about the user that is known to the provider.

ServiceProvider (SP) - it can be any application. In order to access the secure areas of the application the user need to be authenticated and authorized by a IdP

SAML  - Security Assertion Markup Language (SAML, pronounced sam-el) is an XML-based, open-standard data format for exchanging authentication and authorization data between parties, in particular, between an identity provider and a service provider. SAML is a product of the OASIS Security Services Technical Committee.

3. The flow


- the USER  requests a secure area from ServiceProvider
- if the user is not authenticated it is redirected to IdP with some information about SP
- the USER fills his credentials on IdP and after successful authentication is redirected back to SP
- based on the answer from IdP the SP creates the a new user or identify an existing one in his own database and creates a session.

4. Tools


SimpleSAMLphp - it can be used both as IdP and SP, I will be using it only as IdP
lightsaml/SP-Bundle - Symfony Bundle implementing the ServiceProvider

5. Implementation


5.1. Create and configure IdP

Install simpleSAMLphp following the documentation. I've created a virtualhost idp.local and the simpleSAMLphp is available at : http://idp.local/simplesamlphp

Next step is to configure simleSAMLphp as IdentityProvider following the quick guide using the exampleauth:UserPass authentication method.

I'll modify the defined users in config/authsources.php, by replacing the attribute "eduPersonAffilication" with 'roles':

      'student:studentpass' => array(
             'uid' => array('student'),
             'roles' => array('ROLE_USER', 'ROLE_SEF'),
         ),

To make it clear, you have just created a user "student" with password "studentpass". 
Skip the step 6 from the quick guide.
At the step 7 you need to enter the information about ServiceProviders. At this moment they do not exist, but they will, so you can fill the following 2 service providers:

$metadata['http://consumer1.local/saml'] = array(
    'AssertionConsumerService' => 'http://consumer1.local/app_dev.php/saml/login_check',
    'SingleLogoutService'      => 'https://consumer1.local/app_dev.php/logout',
    'simplesaml.nameidattribute' => 'uid',
);

$metadata['http://consumer2.local/saml'] = array(
    'AssertionConsumerService' => 'http://consumer2.local/app_dev.php/saml/login_check',
    'SingleLogoutService'      => 'https://consumer2.local/app_dev.php/logout',
    'simplesaml.nameidattribute' => 'uid',
);

I called them consumers because they consume the authentication service provided by IdP.
Skip steps from 8 to 11.

5.2 Create and configure SPs

Install Symfony and create a virtual host consumer1.local
We will be following the lightsaml/SP-Bundle documentation found here.

At step 7 configure your own entity_id and use the previously created IdP.

 -  First go to your IdP, it should be idp.local/simplesamlphp. Click on the Federation tab, you should see somewhere  SAML 2.0 IdP Metadata. Click on [Show metadata], copy the XML and createa file in your Symfony app: /src/AppBundle/Idp/idp.local.xml and paste the XML.
-  edit app/config.yml



At step 9 (and 11), please see below my security.yml:





Where /secure is a  just route I created for testing purpose. 

Step 10. Basically if the user logged in IdP does not exist in the database of the SP, in this case consumer1.local, it needs to be created.  For this reason a UserCreator class is made. In the documentation this class is able to identify the user id (uid) using a "username mapper" service. I will enhance that by adding an "attributes" mapper which will create an array with the other attributes passed from IdentityProvider. I want to pass a list of roles from IdP to SP.  If you remember I've added a list of 'roles' to my 'student' user. Below you can find a gist with the code for the AttributeMapper










 


Declare the attribute mapper service in app/services.yml:


attribute_mapper:
   class: AppBundle\Security\User\AttributeMapper

Modify the UserCreator class:



Declare the User Creator service injecting in it our Attribute Mapper service:

    user_creator:
        class: AppBundle\Security\User\UserCreator
        arguments:
            - "@=service('doctrine').getManager()"
            - "@lightsaml_sp.username_mapper.simple"
            - "@inno.attribute_mapper"
Also inject the attribute mapper service into the security.authentication.provider.lightsaml_sp service:



    security.authentication.provider.lightsaml_sp:
        class: LightSaml\SpBundle\Security\Authentication\Provider\LightsSamlSpAuthenticationProvider
        arguments:
            - ~ # provider key
            - ~ # user provider
            - ~ # force
            - "@security.user_checker"
            - "@lightsaml_sp.username_mapper.simple" # username mapper
            - ~ # user creator
            - "@inno.attribute_mapper" # attribute mapper
            - ~ # token factory
        abstract: true

5.3 Put your application at work

In browser try to access consumer1.local/app_dev.php/secure, because you are not authenticated
 you should be redirected to http://consumer1.local/app_dev.php/saml/discoveryClick on your IdP,
 you will be redirected to the IdP site where you will fill your user and password (student and 
studentpass. After that you are redirected to the secure page on cosumer1.local.You can check your
 database to see the newly created user. Now copy your Symfony installation and make another 
virtual host consumer2.local. Open the browser and try to access: consumer2.local/app_dev.php/securePick
 your IdP from discovery page, because you are already logged on IdP you will not be asked for 
password again, instead you are redirected to the secure page. Victory!In the next blog post I will
 investigate integrating LDAP with SimpleSAMLphp.