---
# System prepended metadata

title: Authentification FranceConnect dans WebCOnsumerApp via TmsSsoClientBUndle

---

# Authentification FranceConnect dans WebCOnsumerApp via TmsSsoClientBUndle
##  TmsSsoClientBundle

### 1. Configuration
#### a- TmsSsoClientBundle/Resources/config/routing.yml

    france_connect_login:
    path: /login/check-france-connect

#### b- TmsSsoClientBundle/Resources/config/services.yml
    hwi_oauth.resource_owner.tms_france_connect.class:   Tms\Bundle\SsoClientBundle\OAuth\ResourceOwner\TmsFranceConnectResourceOwner
    ...
    wi_oauth.abstract_resource_owner.tms_france_connect:
        class: '%hwi_oauth.resource_owner.tms_france_connect.class%'
        parent: hwi_oauth.abstract_resource_owner.oauth2
        abstract: true
### 2. ResourceOwner
#### a-TmsSsoClientBundle/OAuth/ResourceOwner/TmsFranceConnectResourceOwner.php

##### * Les options
    'client_id' : identifiant fourni par France Connect,
    'client_secret' : code fourni par France Connect,
    'open_id_session_token' : récupere par la fonction getRandomToken(),
    'open_id_session_nonce' => récupere par la fonction getRandomToken(),
    'base_url' : dev => https://fcp.integ01.dev-franceconnect.fr/api/v1/a, prod => https://app.franceconnect.gouv.fr/api/v1/
    'display' => null,
    'sup_scope' => null,
    'scope'               => null,
    'csrf'                => false,
    'user_response_class' => 'HWI\Bundle\OAuthBundle\OAuth\Response\PathUserResponse',
    'auth_with_one_url'   => false,
    'access_token_url'    => false,
    'use_bearer_authorization' => null,
    'infos_url' : dev => https://fcp.integ01.dev-franceconnect.fr/api/v1/userinfo, prod => 	https://app.franceconnect.gouv.fr/api/v1/userinfo
    "certificates" : chemain vers le fichier certificates.pem
    'api_token' : token générer du manager pour authoriser les signin dans SsoManager

##### * Execution:
1- getAuthorizationUrl

    public function getAuthorizationUrl($redirectUri, array $extraParameters = array())
    {

		$state = "token={$this->options['open_id_session_token']}";
		

        $scope = "openid%20".str_replace(",","%20",$this->options['sup_scope']).'%20';
		$info=array("response_type"=>'code',
					"client_id"=> $this->options['client_id'],
					"scope"=>$scope,
					"redirect_uri"=> $redirectUri,
					"state"=>urlencode($state),
					"nonce"=> $this->options['open_id_session_nonce']
		);
		
		$url = $this->getURLforService("authorize");
		foreach($info as $key=>$value){
			$url.=$key."=".$value."&";
		}

       return $url;
    }
    
créer l'url de redirection vers les ervice FranceConnect.
exemple : 

    https://fcp.integ01.dev-franceconnect.fr/api/v1/authorize?client_id=123456&redirect_uri=http://aide-installation-personnel.odr.docker/&scope=openid%20profile%20email%20address%20phone%20preferred_username%20
    
2- Choix du service d'authentification

![](https://i.imgur.com/4ow82l0.png)

3- Se connecter

![](https://i.imgur.com/o8xX5Ya.png)

3- retour sur le site ($redirectUri qui daot etres renseigner sur https://partenaires.franceconnect.gouv.fr/)

![](https://i.imgur.com/dqFM9F7.png)

4- Recuperation du token d'accée

     public function getAccessToken(Request $request, $redirectUri, array $extraParameters = array())
    {
        $parameters = array_merge(array(
            'code'          => $request->query->get('code'),
            'grant_type'    => 'authorization_code',
            'client_id'     => $this->options['client_id'],
            'client_secret' => $this->options['client_secret'],
            'redirect_uri'  => $redirectUri,
        ), $extraParameters);

        

        $response = $this->doGetTokenRequest($this->options['access_token_url'], $parameters);
        $response = $this->getCurlWrapperResponseContent($response);

        

        $this->validateResponseContent($response);
        

        return $response;
    }

Récuperation du token par la fonction "doGetTokenRequest" via l'url de l'option "access_token_url" en ajoutant comme parameters :
    - code : fournit par france connect
    - client_id
    - client_secret
    - redirect_uri
    
    protected function doGetTokenRequest($url, array $parameters = array())
    {
        return $this->httpRequest($url, $parameters);
    }
    
    protected function httpRequest($url, $content = array(), $headers = array(), $method = null)
    {
        if (null === $method) {
            $method = null === $content ? CurlWrapper::METHOD_GET : CurlWrapper::METHOD_POST;
        }

        $curlWrapper = new CurlWrapper();
        $curlWrapper->setServerCertificate($this->options['certificates']);


        $curlWrapper->setPostDataUrlEncode($content);
		$token_url = $this->options['access_token_url'];
		
		$result = $curlWrapper->get($token_url);

        
        return $result;
    }
 
La fonction httpRequest signe les headers de la requette avec la certificat "certificates"

5- Récuperation du contenu de la reponse via la fonction  getCurlWrapperResponseContent

    protected function getCurlWrapperResponseContent($content)
    {

        if (!$content) {
            return array();
        }

        $response = json_decode($content, true);
        if (JSON_ERROR_NONE !== json_last_error()) {
            parse_str($content, $response);
        }


        return $response;
    }
    
6- Validation du contenu de la reponse

    protected function validateResponseContent($response)
    {

        if (isset($response['error_description'])) {
            throw new AuthenticationException(sprintf('OAuth error: "%s"', $response['error_description']));
        }

        if (isset($response['error'])) {
            throw new AuthenticationException(sprintf('OAuth error: "%s"', isset($response['error']['message']) ? $response['error']['message'] : $response['error']));
        }

        if (!isset($response['access_token'])) {
            throw new AuthenticationException('Not a valid access token.');
        }
    }
    
    
    
7- Recuperation des infos utilisateur via la fonction getUserInformation

    public function getUserInformation(array $accessToken, array $extraParameters = array())
    {
        $curlWrapper = new CurlWrapper();
		$curlWrapper->setServerCertificate($this->options['certificates']);
		$curlWrapper->addHeader("Authorization", "Bearer ".$accessToken['access_token']);
		$user_info_url = $this->getURLforService("userinfo");
		$result = $curlWrapper->get($user_info_url);

        

        $response = $this->getUserResponse();
        
        $response->setResponse($result);
        $response->setResourceOwner($this);
        $response->setOAuthToken(new OAuthToken($accessToken));
		return $response;
    }


#### d- TmsSsoClientBundle/Manager/CurlWrapper.php

    <?php
    /**
     * Created by PhpStorm.
     * User: tveron
     * Date: 01/08/2016
     * Time: 12:00
     */

    namespace Tms\Bundle\SsoClientBundle\Manager;


    class CurlWrapper
    {
        const POST_DATA_SEPARATOR = "\r\n";

    private $curlHandle;

    private $lastError;

    private $postData;

    private $postFile;

    private $postFileProperties;


    const METHOD_OPTIONS = 'OPTIONS';
    const METHOD_GET     = 'GET';
    const METHOD_HEAD    = 'HEAD';
    const METHOD_POST    = 'POST';
    const METHOD_PUT     = 'PUT';
    const METHOD_DELETE  = 'DELETE';
    const METHOD_PATCH   = 'PATCH';

    public function __construct($proxy = null)
    {
        $this->curlHandle = curl_init();
        $this->setProperties(CURLOPT_RETURNTRANSFER, 1);
        $this->setProperties(CURLOPT_FOLLOWLOCATION, 1);
        $this->setProperties(CURLOPT_MAXREDIRS, 5);
        if(!empty($proxy))
        {
            $this->setProperties(CURLOPT_HTTPPROXYTUNNEL, 1);
            $this->setProperties(CURLOPT_PROXY, $proxy);
        }
        $this->postFile = array();
        $this->postData = array();
        curl_setopt($this->curlHandle, CURLOPT_SSL_VERIFYPEER, true);
    }

    public function __destruct()
    {
        curl_close($this->curlHandle);
    }

    public function httpAuthentication($username, $password)
    {
        $this->setProperties(CURLOPT_USERPWD, "$username:$password");
        $this->setProperties(CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
    }

    public function addHeader($name, $value)
    {
        $this->setProperties(CURLOPT_HTTPHEADER, array("$name: $value"));
    }

    public function getLastError()
    {
        return $this->lastError;
    }

    private function setProperties($properties, $values)
    {
        curl_setopt($this->curlHandle, $properties, $values);
    }

    public function setAccept($format)
    {
        $curlHttpHeader[] = "Accept: $format";
        $this->setProperties(CURLOPT_HTTPHEADER, $curlHttpHeader);
    }

    public function dontVerifySSLCACert()
    {
        $this->setProperties(CURLOPT_SSL_VERIFYHOST, 0);
        $this->setProperties(CURLOPT_SSL_VERIFYPEER, 0);
    }

    public function setServerCertificate($serverCertificate)
    {
        $this->setProperties(CURLOPT_CAINFO, $serverCertificate);
        $this->setProperties(CURLOPT_SSL_VERIFYPEER, 0);
    }

    public function setClientCertificate($clientCertificate, $clientKey, $clientKeyPassword)
    {
        $this->setProperties(CURLOPT_SSLCERT, $clientCertificate);
        $this->setProperties(CURLOPT_SSLKEY, $clientKey);
        $this->setProperties(CURLOPT_SSLKEYPASSWD, $clientKeyPassword);
    }

    public function get($url)
    {
        $this->setProperties(CURLOPT_URL, $url);
        if ($this->postData || $this->postFile) {
            $this->curlSetPostData();
        }
        curl_setopt($this->curlHandle, CURLINFO_HEADER_OUT, true);

        $output = curl_exec($this->curlHandle);

        //print_r(curl_getinfo($this->curlHandle,CURLINFO_HEADER_OUT));

        $this->lastError = curl_error($this->curlHandle);
        if ($this->lastError) {
            $this->lastError = "Erreur de connexion au serveur : " . $this->lastError;
            return false;
        }
        return $output;
    }

    public function addPostData($name, $value)
    {
        if (!isset($this->postData[$name])) {
            $this->postData[$name] = array();
        }

        $this->postData[$name][] = $value;
    }

    public function setPostDataUrlEncode(array $post_data)
    {
        $pd = array();
        foreach ($post_data as $k => $v) {
            $pd[] = "$k=$v";
        }
        $pd = implode("&", $pd);
        $this->setProperties(CURLOPT_POST, true);
        $this->setProperties(CURLOPT_POSTFIELDS, $pd);
        $this->setProperties(CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
    }


    public function addPostFile($field, $filePath, $fileName = false, $contentType = "application/octet-stream", $contentTransferEncoding = false)
    {
        if (!$fileName) {
            $fileName = basename($filePath);
        }
        $this->postFile[$field][$fileName] = $filePath;
        $this->postFileProperties[$field][$fileName] = array($contentType, $contentTransferEncoding);
    }

    private function getBoundary()
    {
        return '----------------------------' .
        substr(sha1('CurlWrapper' . microtime()), 0, 12);
    }

    private function curlSetPostData()
    {
        $this->setProperties(CURLOPT_POST, true);
        if ($this->isPostDataWithSimilarName()) {
            $this->curlSetPostDataWithSimilarFilename();
        } else {
            $this->curlPostDataStandard();
        }
    }

    private function isPostDataWithSimilarName()
    {
        $array = array();

        foreach ($this->postData as $name => $multipleValue) {
            foreach ($multipleValue as $data) {
                if (isset($array[$name])) {
                    return true;
                }
                $array[$name] = true;
            }
        }
        foreach ($this->postFile as $name => $multipleValue) {
            foreach ($multipleValue as $data) {
                if (isset($array[$name])) {
                    return true;
                }
                $array[$name] = true;
            }
        }
    }

    private function curlPostDataStandard()
    {
        $post = array();
        foreach ($this->postData as $name => $multipleValue) {
            foreach ($multipleValue as $value) {
                $post[$name] = $value;
            }
        }
        foreach ($this->postFile as $name => $multipleValue) {
            foreach ($multipleValue as $fileName => $filePath) {
                $post[$name] = "@$filePath;filename=$fileName";
            }
        }
        curl_setopt($this->curlHandle, CURLOPT_POSTFIELDS, $post);
    }

    private function curlSetPostDataWithSimilarFilename()
    {
        $boundary = $this->getBoundary();

        $body = array();

        foreach ($this->postData as $name => $multipleValue) {
            foreach ($multipleValue as $value) {
                $body[] = "--$boundary";
                $body[] = "Content-Disposition: form-data; name=$name";
                $body[] = '';
                $body[] = $value;
            }
        }


        foreach ($this->postFile as $name => $multipleValue) {
            foreach ($multipleValue as $fileName => $filePath) {
                $body[] = "--$boundary";
                    $body[] = "Content-Disposition: form-data; name=$name; filename=\"$fileName\"";
                    $body[] = "Content-Type: {$this->postFileProperties[$name][$fileName][0]}";
                    if ($this->postFileProperties[$name][$fileName][1]) {
                        $body[] = "Content-Transfer-Encoding: {$this->postFileProperties[$name][$fileName][1]}";
                    }
                    $body[] = '';
                    $body[] = file_get_contents($filePath);
                }
            }

            $body[] = "--$boundary--";
            $body[] = '';

            $content = join(self::POST_DATA_SEPARATOR, $body);


            $curlHttpHeader[] = 'Content-Length: ' . strlen($content);
            $curlHttpHeader[] = 'Expect: 100-continue';
            $curlHttpHeader[] = "Content-Type: multipart/form-data; boundary=$boundary";

            $this->setProperties(CURLOPT_HTTPHEADER, $curlHttpHeader);
            $this->setProperties(CURLOPT_POSTFIELDS, $content);
        }

        public function getHTTPCode()
        {
            return curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE);
        }
    }
    
    
#### e- TmsSsoClientBundle/Model/User.php

##### * Attributs
    
     /**
     * France Connect identifier
     *
     * @var string
     */
    protected $franceConnectId = null;
    
    /**
     * user phone
     *
     * @var string
     */
    protected $phone   = null;


    /**
     * user birthdate
     *
     * @var string
     */
    protected $birthdate   = null;


    /**
     * user birthplace
     *
     * @var string
     */
    protected $birthplace   = null;
    
##### * Récuperation des info utilisateur "setFromResponse"

    } else if ($resourceOwner instanceof TmsFranceConnectResourceOwner) {
            $this->franceConnectId  = $raw['sub'];
            $this->username  = isset($raw['email']) ? $raw['email'] : null;
            $this->gender    = isset($raw['gender']) ? (('male' == $raw['gender']) ? 1: 2) : 0;
            $this->firstName = isset($raw['given_name']) ? $raw['given_name'] : null;
            $this->lastName  = isset($raw['family_name']) ? $raw['family_name'] : null;
            $this->phone  = isset($raw['phone']) ? $raw['phone'] : null;
            $this->birthdate  = isset($raw['birthdate']) ? $raw['birthdate'] : null;
            $this->birthplace  = isset($raw['birthplace']) ? $raw['birthplace'] : null;
        } 
        
## WebConsumerApp

### 1- Configuration

#### a- WebConsumerApp/app/config/config.yml
    
    tms_france_connect:
            type:  tms_france_connect
            scope: "email profile"
            options:
                display: popup
                base_url: "%dgafp.aipe.france_connect.base_url%"
                sup_scope: "%dgafp.aipe.france_connect.sup_scope%"
                access_token_url: "%dgafp.aipe.france_connect.access_token_url%"
                infos_url: "%dgafp.aipe.france_connect.infos_url%"
                certificates: "%dgafp.aipe.france_connect.certificates%"
            identity:
                selector: tms_web_security.identity.customer
                tokens:
                    dgafp:
                        client_id: "%dgafp.aipe.france_connect.id%"
                        client_secret: "%dgafp.aipe.france_connect.secret%"
                        api_token: "%dgafp.aipe.france_connect.api_token%"
                        
#### b- WebConsumerApp/app/config/parameters.yml

    dgafp.aipe.france_connect.id: 1553bc08000a5c65e18a2d8481e8e1b8991f3c253445331141eb4f13e3b89b5b
    dgafp.aipe.france_connect.secret: ea1ce2c9d09b2be59e5fae9930de7c542e5b807f18e38dc2e1f40ac6e05397c8
    dgafp.aipe.france_connect.base_url: 'https://fcp.integ01.dev-franceconnect.fr/api/v1/'
    dgafp.aipe.france_connect.sup_scope: 'profile,email,preferred_username,birthdate,birthplace'
    dgafp.aipe.france_connect.access_token_url: 'https://fcp.integ01.dev-franceconnect.fr/api/v1/token'
    dgafp.aipe.france_connect.infos_url: 'https://fcp.integ01.dev-franceconnect.fr/api/v1/userinfo'
    dgafp.aipe.france_connect.certificates: /home/bouraoui/Documents/certificates/certificates.pem
    dgafp.aipe.france_connect.api_token: 1g89fhc3opokkggkwckg8gs48k08go840ggs848c0kgwg88ggc
    
#### c- WebConsumerApp/app/config/routing.yml

    france_connect_login:
        path: /oauth/login/check-france-connect
        
#### d- WebConsumerApp/app/config/security.yml

     oauth_secured_area:
            pattern: ^/
            oauth:
                resource_owners:
                    tms:          /oauth/login/check-tms
                    tms_facebook: /oauth/login/check-facebook
                    tms_google:   /oauth/login/check-google
                    tms_france_connect:   /oauth/login/check-france-connect
                    external:     /oauth/login/check-external
                check_path:   tms_login
                login_path:   tms_websecurity_connect_login
                failure_path: tms_websecurity_connect_login
                success_handler: tms_web_portal.listener.login
                oauth_user_provider:
                    service: da_oauth_client.user_provider.memory
            logout:
                success_handler: tms_web_portal.listener.logout
            anonymous: ~
#### e-WebConsumerApp/src/Tms/WebPortalBundle/Resources/themes/dgafp.aipe/views/User/login.html.twig

    <a href="{{ host ~ path("hwi_oauth_service_redirect", {service: "tms_france_connect"}) }}" title="Se connecter avec FranceConnect" class="button social france-connect "></a>