📜 ⬆️ ⬇️

Implementing Single Sign On in a Symfony2 Application

What is Single Sign On?


Single Sign On is a technology by which a user, being authenticated on a certification authority (hereinafter Identity Provider, IdP) , will be automatically authenticated on another service (hereinafter referred to as Service Provider, SP or Consumer [1-N]) of this company.

The Single Sign On mechanism is used by such sites as HabraHabr , Yandex , Google . The advantages of this approach to user authentication are obvious:


The minuses, of course, follow, as always, from the pros:
')

Despite this, from a business point of view, as well as user experience, the implementation of this functionality outweighs all the disadvantages, and the epic on the implementation of SSO in the company begins.

Before proceeding with the implementation of SSO in the company, it would be good to make sure that you are well aware of what it is:


and even better, how to apply them yourself .

This is important because incorrect implementation of SSO can lead to critical errors in all services that are connected to SSO: from compromising user data in one service, ending with hijacking an account in IdP, which leads to compromise of data in all services.

On Habré there is another great article on the basic principles of working with cookies and how to properly set cookies so as not to remain without pants: habrahabr.ru/company/mailru/blog/228997 .

So, after getting acquainted with the basic theory: what is SSO, security aspects that are associated with this task - we can begin to implement it.

How it will work


In general, authentication will take place in the following scenario:

image

Consider the scenario when the user goes to any page protected by authorization through the bookmarks (paragraph 1 in the diagram) .
Next, in Symfony2, the Entry point mechanism is activated and redirects us to our IdP, where we have to drop the OTP. There are several scenarios for the development of events:

  1. The user is authenticated to IdP, then IdP simply docks into the OTP redirection chain ( step 3 in the diagram, green line)
  2. The user is not authenticated to IdP, then he should be sent to the login / password input form (paragraph 3 on the diagram, red line)
  3. The user generally sees us for the first time, but wants to register and goes to the registration form (At this point, the SP from which he came is saved on the IdP session.)

After the user, for example, has registered, he should be redirected to step 3 along the green line for OTP validation to SP, from which he came to us on IdP. When we validate OTP on SP, we make a trusted REST request to our IdP to make sure that such OTP really exists and has not expired in time. At this point, the REST service should invalidate this OTP. Put Lok, this operation must be atomic . Further requests with this OTP should return either HTTP 400 or HTTP 404 for SP.

In the case where IdP responded that such an OTP exists and is valid, SP authenticates the user by issuing a PreAuthenticatedToken to it.

The output will work as follows:

image

Please note that this type of output is considered from the point of view of the beginning of the process on the SP. This is important because the user will be returned to where he started the operation.

Suppose the user was on a certain page / secured_area and clicked on "Exit". At this point, a local logout occurs within SP. Then we go to IdP at a special URL / sso / logout , which will manage the exit from all services for this user. Since since the user has already arrived with SP, then IdP selects the next service that is in the company, and sends it to make an exit. That service, in turn, again upon completion, sends us to IdP and if the services are over, it performs a local exit (Section 5 in the diagram). After the user is sent back to the SP, from which he began to make an exit.

There is another scenario in which the user begins the process of exiting not from SP but from IdP. And it looks like this:

image

Certification Authority (IdentityProvider)


To make a certification center, you must first select an application in your company that will be responsible for this, like it was done in Yandex (Yandex.Passport) or in Google (Google Accounts).

In this application we will install the first part: SingleSignOnIdentityProviderBundle

SingleSignOnIdentityProviderBundle is responsible for:


We put through the composer:

php composer.phar require "korotovsky/sso-idp-bundle:~0.2.0" 

Next, update the dependencies and write our bundle in AppKernel:
app / AppKernel.php
 // app/AppKernel.php $bundles[] = new \Krtv\Bundle\SingleSignOnIdentityProviderBundle\KrtvSingleSignOnIdentityProviderBundle(); 

We connect routes / sso / login and / sso / logout from the bundle:
app / config / routing.yml
 # app/config/routing.yml: sso: resource: . type: sso 

Now configure the IdP bundle:
app / config / config.yml
 # app/config/config.yml: krtv_single_sign_on_identity_provider: host: idp.example.com #   IdP  host_scheme: http #   IdP . login_path: /sso/login/ # ,    OTP logout_path: /sso/logout # ,        services: - consumer1 #   SP   ,       otp_parameter: _otp #  OTP  secret_parameter: secret #    Dependency Injection     , #         . 

Rule security.yml:
app / config / security.yml
 # app/config/security.yml security: access_control: - { path: ^/sso/login$, roles: [ROLE_USER, IS_AUTHENTICATED_FULLY] } 

Now you need to register SP in our bundle, for this we will create a class that immenment the \ Krtv \ Bundle \ SingleSignOnIdentityProviderBundle \ Manager \ ServiceProviderInterface interface and register it in the service container using the tag
src / Acme / Bundle / AppBundle / Resources / config / security.yml
 services: acme_bundle.sso.consumer1: class: Acme\Bundle\AppBundle\Sso\ServiceProviders\ServiceProvider1 tags: - { name: sso.service_provider, service: consumer1 } 

Bundle Public API

This bundle registers with Dependency Injection several services that will be useful when customizing SSO processes in the final project.


This completes the IdP setup, proceed to the SP setup.

The application in which you want to authenticate the user via IdP should set itself SingleSignOnServiceProviderBundle

SingleSignOnServiceProviderBundle is responsible for:


We put through the composer:

 php composer.phar require "korotovsky/sso-sp-bundle:~0.2.0" 

Next, update the dependencies and write our bundle in AppKernel:
app / AppKernel.php
 // app/AppKernel.php $bundles[] = new \Krtv\Bundle\SingleSignOnServiceProviderBundle\SingleSignOnServiceProviderBundle(); 

We connect the route / otp / validate / for OTP validation:
app / config / routing.yml
 # app/config/routing.yml: otp: # this needs to be the same as the check_path, specified later on in security.yml path: /otp/validate/ 

Now configure the IdP bundle:
app / config / config.yml
 # app/config/config.yml: krtv_single_sign_on_service_provider: host: idp.example.com #   IdP  host_scheme: http #   IdP . login_path: /sso/login/ #     OTP # Configuration for OTP managers otp_manager: name: http managers: http: provider: service # Active provider for HTTP OTP manager providers: # Available HTTP providers service: id: acme_bundle.your_own_fetch_service.id guzzle: client: acme_bundle.guzzle_service.id resource: http://idp.example.com/internal/v1/sso otp_parameter: _otp #  OTP  secret_parameter: secret #    Dependency Injection     , #         . 

In order for us to have an “honest” SSO, we need to choose the http method as a manager, and choose a service as a provider. To do this, you must implement the Krtv \ SingleSignOn \ Manager \ Http \ Provider \ ProviderInterface interface.

Rule security.yml:
app / config / security.yml
 # app/config/security.yml firewalls: main: pattern: ^/ sso: require_previous_session: false provider: main check_path: /otp/validate/ # Same as in app/config/routing.yml sso_scheme: http # Required sso_host: idp.example.com # Required sso_otp_scheme: http # Optional sso_otp_host: consumer1.com # Optional sso_failure_path: /login sso_path: /sso/login/ # SSO endpoint on IdP. sso_service: consumer1 # Consumer name logout: invalidate_session: true path: /logout target: http://idp.example.com/sso/logout?service=consumer1 

Bundle Public API

This bundle registers with Dependency Injection several services that will be useful when customizing SSO processes in the final project.


This completes the configuration of our applications.

To keep track of updates to bundles, put asterisks for them:

Source: https://habr.com/ru/post/260183/


All Articles