diff --git a/docs/Client_Setup.md b/docs/Client_Setup.md
new file mode 100644
index 0000000..51917c0
--- /dev/null
+++ b/docs/Client_Setup.md
@@ -0,0 +1,327 @@
+# Client setup
+## Add needed dependencies
+```bash
+ composer require nelmio/cors-bundle
+ composer require knpuniversity/oauth2-client-bundle
+```
+
+## Configure the bundle
+### nelmio/cors-bundle
+```yaml
+# config/packages/nelmio_cors.yaml
+nelmio_cors:
+ defaults:
+ origin_regex: true
+ allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']
+ allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
+ allow_headers: ['Content-Type', 'Authorization']
+ expose_headers: ['Link']
+ max_age: 3600
+ paths:
+ '^/token$':
+ origin_regex: true
+ allow_origin: ['*']
+ allow_headers: ['Content-Type', 'Authorization']
+ allow_methods: ['POST', 'OPTIONS']
+ allow_credentials: true
+ max_age: 3600
+ '^/authorize$':
+ origin_regex: true
+ allow_origin: ['*']
+ allow_headers: ['Content-Type', 'Authorization']
+ allow_methods: ['GET', 'POST', 'OPTIONS']
+ allow_credentials: true
+ max_age: 3600
+```
+### knpuniversity/oauth2-client-bundle
+```yaml
+# config/packages/knpu_oauth2_client.yaml
+knpu_oauth2_client:
+ clients:
+ # configure your clients as described here: https://github.com/knpuniversity/oauth2-client-bundle#configuration
+ sudalys:
+ type: generic
+ provider_class: Sudalys\OAuth2\Client\Provider\Sudalys
+ client_id: '%env(OAUTH2_CLIENT_ID)%'
+ client_secret: '%env(OAUTH2_CLIENT_SECRET)%'
+ redirect_route: uri # The route to redirect to after authentication (must match the one in the server DB uri DB)
+ provider_options: {
+ domain:
+ }
+ use_state: false
+```
+
+### .env
+```dotenv
+# .env
+# CORS
+CORS_ALLOW_ORIGIN=http://*.your domain/*'
+# OAUTH2
+OAUTH2_CLIENT_ID=
+OAUTH2_CLIENT_SECRET=
+```
+
+Copy and paste the client library then modify the conposer.json autoloard directive to include the new library
+```json
+ "autoload": {
+ "psr-4": {
+ "App\\": "src/",
+ "Sudalys\\OAuth2\\Client\\": "libs/sudalys/oauth2-client/src"
+ }
+ },
+```
+
+```php
+clientRegistry = $clientRegistry;
+ $this->em = $em;
+ $this->router = $router;
+ $this->urlGenerator = $urlGenerator;
+ }
+
+ public function start(Request $request, AuthenticationException $authException = null): Response
+ {
+ // Use the KnpU client to generate the correct authorization URL,
+ // including state / redirect_uri / scope / pkce as configured.
+ $client = $this->getSudalysClient();
+
+ // Option A: let the client use the configured redirect uri and default scopes:
+ return $client->redirect();
+
+ // Option B (explicit): specify scopes and an explicit redirect_uri (absolute URL)
+ // $redirectUri = $this->urlGenerator->generate('sudalys_check', [], UrlGeneratorInterface::ABSOLUTE_URL);
+ // return $client->redirect(['openid', 'profile'], ['redirect_uri' => $redirectUri]);
+ }
+
+
+ public function supports(Request $request): ?bool
+ {
+ // If your OAuth redirect route is named 'sudalys_check', check by route:
+ if ($request->attributes->get('_route') === 'sudalys_check') {
+ return true;
+ }
+
+ // fallback: also support requests containing the authorization code
+ return (bool) $request->query->get('code');
+ }
+
+ public function authenticate(Request $request): Passport
+ {
+ $client = $this->getSudalysClient();
+ $accessToken = $this->fetchAccessToken($client);
+ $session = $request->getSession();
+ $session->set('access_token', $accessToken->getToken());
+
+ // Stocker également le refresh token s'il est disponible
+ if ($accessToken->getRefreshToken()) {
+ $session->set('refresh_token', $accessToken->getRefreshToken());
+ }
+ return new SelfValidatingPassport(
+ new UserBadge($accessToken->getToken(), function () use ($accessToken, $client) {
+ //show in log the access token
+ $sudalysSsoUser = $client->fetchUserFromToken($accessToken);
+
+ $ssoId = $sudalysSsoUser->getId();
+
+ /*
+ * On regarde si le token est valide
+ */
+ if($accessToken->getExpires() > time()) {
+ // Token valide, on regarde si l'utilisateur existe en bdd locale
+ /** @var User $userInDatabase */
+ $user = $this->em->getRepository(User::class)->findOneBy(['ssoId' => $ssoId]);
+
+ /**
+ * on cree l'utilisateur s'il n'existe pas
+ **/
+ if (!$user) {
+ $user = new User();
+ $user->setEmail($sudalysSsoUser->getEmail());
+ $user->setName($sudalysSsoUser->getName());
+ $user->setSurname($sudalysSsoUser->getSurname());
+ $user->setSsoId($sudalysSsoUser->getId());
+ $this->em->persist($user);
+ }else{
+ // On met a jour l'utilisateur
+ $user->setEmail($sudalysSsoUser->getEmail());
+ $user->setName($sudalysSsoUser->getName());
+ $user->setSurname($sudalysSsoUser->getSurname());
+ $this->em->persist($user);
+ }
+ $this->em->flush();
+ return $user;
+ }
+ })
+ );
+ }
+
+ public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
+ {
+ // change "app_homepage" to some route in your app
+ $targetUrl = $this->router->generate('app_index');
+ return new RedirectResponse($targetUrl);
+ }
+
+ public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
+ {
+ $message = strtr($exception->getMessageKey(), $exception->getMessageData());
+ return new Response($message, Response::HTTP_FORBIDDEN);
+ }
+
+ /**
+ *
+ */
+ private function getSudalysClient()
+ {
+ return $this->clientRegistry->getClient('sudalys');
+ }
+
+}
+
+```
+
+```php
+namespace App\Security\SsoAuthenticator;
+
+ public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
+ {
+ // change "app_homepage" to some route in your app
+ $targetUrl = $this->router->generate('your redirect route');
+ return new RedirectResponse($targetUrl);
+ }
+```
+
+### Security.yaml
+```yaml
+ app_user_provider:
+ entity:
+ class: App\Entity\User
+ property: email
+
+ firewalls:
+ main:
+ lazy: true
+ provider: app_user_provider
+ custom_authenticators:
+ - App\Security\SsoAuthenticator
+ entry_point: App\Security\SsoAuthenticator
+ logout:
+ path: app_logout
+ target: app_after_logout
+ invalidate_session: true
+ delete_cookies: ['PHPSESSID']
+
+ access_control:
+ - { path: ^/sso/login, roles: PUBLIC_ACCESS }
+ - { path: ^/sso/check, roles: PUBLIC_ACCESS }
+ - { path: ^/, roles: IS_AUTHENTICATED_FULLY }
+```
+### Setup oauth controller
+```php
+getClient('sudalys')->redirect();
+ }
+
+ #[Route('/sso/check', name: 'sudalys_sso_check')]
+ public function connectCheckAction(Request $request)
+ {
+ return $this->redirectToRoute('app_index');
+ }
+
+
+ #[Route('/logout', name: 'app_logout')]
+ public function logout(): void
+ {
+ throw new \Exception('This should never be reached!');
+ }
+
+ #[Route('/logout-redirect', name: 'app_after_logout')]
+ public function afterLogout(): RedirectResponse
+ {
+ // SSO logout URL — adjust if necessary
+ $ssoLogout = 'http://portail.solutions-easy.moi/sso_logout';
+
+ return new RedirectResponse($ssoLogout);
+ }
+}
+```
+# Server setup
+## Create OAuth2 client
+```cmd
+php bin/console league:oauth2-server:create-client --redirect-uri="http://your-client-domain/sso/check" --scope="openid" --scope="profile" --scope="email" --grant-type=authorization_code
+```
+If there is a scope or grand error, delete the client do the following first
+```cmd
+php bin/console league:oauth2-server:delete-client
+```
+Identifier can be found in the database oauth2_client table
+The recreate the client and enter the scopes and grant types after creating the client directly in the db
+```text
+scopes = email profile openid
+grants = authorization_code
+```