From b2bb9fc78bfbc63709caa08fe842c3d41b177092 Mon Sep 17 00:00:00 2001 From: Charles Date: Wed, 18 Feb 2026 16:27:29 +0100 Subject: [PATCH] set up doc for API --- docs/API.md | 194 +++++++++++++++++++++++++++++++++++++++++++ docs/Client_Setup.md | 4 +- 2 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 docs/API.md diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..dc504f3 --- /dev/null +++ b/docs/API.md @@ -0,0 +1,194 @@ +# Intro + +The Api made some changes to the current structure of the project. These changes include the following: +- **Security** : the security.yaml file has been updated both on the server and the client side +- **Controllers**: Controllers need to be updated on the client side +- **Services**: Services and token management need updates on the client side +- **Health**: I now want to commit a Talon E over a wall IRL +- **Entities**: Entities need to be updated on the client side to include a new field (sso_id). + - This field will be used a lot, so add it to an entity if you are going to work with the API and the SSO. PLEASE. +- **Roles**: A new role was added because we are doing M2M. Only God know how that work now, but it works, so I might start praying from now on. + + +# Security +new firewall was added. Keep the same structure for future firewalls. +### Client side +```yaml + api_project: + pattern: ^/api/v1/project #ofc, this is an example, please THINK and change the name + stateless: true + access_token: + token_handler: App\Security\SsoTokenHandler +``` +Same thing, new firewall was added. +### Server side +```yaml + api_token_validation: + pattern: ^/api/validate-token #this is NOT an example. DON'T change or it will all go to sh.t + stateless: true + oauth2: true +``` +```yaml +# A rajouter dans l'access_control aussi !!! IMPORTANT !!! + - { path: ^/api/validate-token, roles: PUBLIC_ACCESS } +``` + +# Controllers +On the client side, create a new controller for the API. This controller need will work in a REST manner. +The route should be as follows: +```php +#[Route('/api/v1/project', name: 'api_project')] //ofc, this is an example, please THINK and change the name +``` +Here is a full example of a controller with the create method. +```php +getContent(), true, 512, JSON_THROW_ON_ERROR); + $projet = new Projet(); + + $entity = $this->entityManager->getRepository(Entity::class)->find($data['entity_id']); + $precision= $data['timestamp']; + $validPrecisions = array_map(fn($case) => $case->value, TimestampPrecision::cases()); + if (!in_array($precision, $validPrecisions, true)) { + return $this->json(['success' => false, 'message' => 'Précision d\'horodatage invalide.'], 400); + } + + try { + $timestampPrecision = TimestampPrecision::from($precision); + $projet->setTimestampPrecision($timestampPrecision); + $projet->setProjet($data['projet']); + $projet->setEntityId($entity); + $projet->setBdd($data['bdd']); + $projet->setIsactive($data['isactive']); + $projet->setLogo($data['logo']); + $projet->setDeletionAllowed($data['deletion']); + $projet->setSsoId($data['id']); // c'est l'id du projet dans le portail, pas la bdd local + + $this->entityManager->persist($projet); + $this->entityManager->flush(); + return new JsonResponse(['message' => 'Project created successfully', 'project_id' => $projet->getId()], 201); + }catch ( \Exception $e){ + return new JsonResponse(['error' => 'Failed to create project: ' . $e->getMessage()], 500); + } + } +``` + +# Services +So, now we are getting into the thick of it. We are just COOK 🍗. +We implement a new pretty service called SsoTokenHandler. This is used to get the token received from the portal request, and validate it. +It is validaded by doing a call back to the SSO and asking if the token is valid. ( we create a new token for the SSO so it handles M2M) +```php +httpClient->request('GET', $this->ssoUrl . '/api/validate-token', [ + 'headers' => ['Authorization' => 'Bearer ' . $accessToken] + ]); + + // If the SSO redirects, HttpClient might follow it to the login page. + // Let's see the first response code. + if ($response->getStatusCode() !== 200) { + // Log the content to see the HTML "Login" page that is causing the JSON error + // dump($response->getContent(false)); + throw new BadCredentialsException(); + } + } catch (\Exception $e) { + // This will show you if you are hitting a 302 Redirect + throw new BadCredentialsException('SSO returned invalid response: ' . $e->getMessage()); + } + + if (200 !== $response->getStatusCode()) { + throw new BadCredentialsException('Invalid SSO Token'); + } + + $data = $response->toArray(); + + // 2. Identify if it's a User or a Machine + $identifier = $data['email'] ?? 'SYSTEM_SSO_SERVER'; + + // 3. Return the badge with a "loader" closure + return new UserBadge($identifier, function($userIdentifier) use ($data) { + // If it's the SSO server calling, give it a specific role + if ($userIdentifier === 'SYSTEM_SSO_SERVER') { + return new InMemoryUser($userIdentifier, null, ['ROLE_API_INTERNAL']); + } + + // Otherwise, let the normal user provider handle it (for standard users) + // You might need to inject your actual UserProvider here if needed + return new InMemoryUser($userIdentifier, null, ['ROLE_USER']); + }); + } +} +``` + +# Important note 1 +we need to add the portal url to the .env and declare it as a parameter in services.yaml +```dotenv +SSO_URL='http://portail.solutions-easy.moi' +``` +```yaml +parameters: + sso_url: '%env(SSO_URL)%' + + App\Security\SsoTokenHandler: + arguments: + $ssoUrl: '%sso_url%' +``` +# Server side +On the server side, we need to create a new client, which will be himself, same as earlier, create it, and boom we are good. +The validate route is already created, so dw abt it. + +```cmd +php bin/console league:oauth2-server:create-client sso_internal_service --grant-type "client_credentials" +``` + +now, copy the identifier, and paste it in the .env file +```dotenv +OAUTH_SSO_IDENTIFIER='sso-own-identifier' +``` +and we are smart so what do we do? we add it to the services.yaml +```yaml +parameters: + oauth_sso_identifier: '%env(OAUTH_SSO_IDENTIFIER)%' + + App\Service\SSO\ProjectService: + arguments: + $appUrl: '%app_url%' + $clientIdentifier: '%oauth_sso_identifier%' +``` + +We should be good now ( I hope ). Open the portal, try your call and check if it works, if it doesn't, check the logs and debug, you are a dev for a reason, so use your brain and debug. +If it still doesn't work, start praying, because I have no idea what to do anymore, but it works on my side, so it should work on yours, if not, well, I don't know what to say. Good luck. +Jokes aside, bugs often come from security problem, if the client returns a 401 error, it can be for multiple reasons and not necessarily because of the token but maybe because of the token validation. +Another commun bug is mismatching of the data you send, so double check. GLHF ( you won't have fun, but good luck anyway ) diff --git a/docs/Client_Setup.md b/docs/Client_Setup.md index 51917c0..54c8568 100644 --- a/docs/Client_Setup.md +++ b/docs/Client_Setup.md @@ -320,8 +320,10 @@ If there is a scope or grand error, delete the client do the following first 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 +To 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 ``` + +