FlorAccess est avant tout une API d'authentification utilisée par mes autres projets. Elle permet un accès sécurisé et sans mot de passe à toutes mes applications, avec un seul compte utilisateur. FlorAccess inclut aussi une application d'administration, accessible uniquement par moi, qui me permet de gérer les utilisateurs.
FlorAccess est avant tout une API d'authentification utilisée par mes autres projets. Elle permet un accès sécurisé et sans mot de passe à toutes mes applications, avec un seul compte utilisateur. FlorAccess inclut aussi une application d'administration, accessible uniquement par moi, qui me permet de gérer les utilisateurs.
On pourrait comparer FlorAccess au bouton "Se connecter avec Google" que l'on trouve sur de nombreux sites web. Cependant, au lieu d'utiliser Google comme fournisseur d'identité, j'ai créé mon propre fournisseur d'identité personnalisé.
Le fait de concevoir ma propre API d'authentification me permet notamment de bâtir un véritable écosystème, au sein duquel toutes mes applications reposant sur ce service peuvent s'interconnecter.
J'ai créé, testé et déployé FlorAccess de manière totalement autonome. Être le seul acteur de ce projet signifie que j'ai eu le contrôle total sur toutes les décisions de conception et de développement. Cependant, cela implique également que j'ai dû faire face à l'ensemble des défis techniques par moi-même, sans pouvoir m'appuyer sur l'aide directe d'un tiers.
J'ai tout de même eu l'occasion de discuter de FlorAccess avec d'autres développeurs dans mon cadre professionnel afin de recueillir leurs avis sur le concept, mais je n'ai bénéficié d'aucune aide concrète pour le développement de l'API.
Avant d'aborder FlorAccess en détail, il convient de se pencher sur ma toute première application, Econoris. C'est en effet ce projet qui m'a poussé à concevoir ce système d'authentification centralisé.
Econoris est une application de gestion de budget quotidien. Le but était de proposer une solution ultra-simple pour m'aider au quotidien, tout en ciblant des personnes désireuses de suivre leurs finances mais qui ne sont pas familières avec les concepts de gestion budgétaire ou avec l'outil informatique en général.
Si je devais définir un persona pour Econoris, ce serait une mère de famille disposant d'un revenu mensuel inférieur à 1 600 € avec deux enfants à charge. Ayant des difficultés financières en fin de mois, elle recherche une application épurée pour l'accompagner efficacement.
Avec une telle cible, l'expérience globale doit être fluide, y compris l'étape d'authentification. Pour autant, la sécurité reste primordiale, s'agissant de données hautement sensibles. Je ne souhaitais pas non plus dépendre d'un fournisseur tiers (comme Google, Microsoft ou Facebook), afin d'éviter d'ajouter de la complexité technique et de préserver la confidentialité des données.
C'est face à ces contraintes que j'ai décidé de créer FlorAccess : une API d'authentification simple, hautement sécurisée et totalement indépendante.
La philosophie de FlorAccess repose sur la simplicité d'utilisation. Je voulais une API d'authentification sans mot de passe, réduisant au maximum le nombre d'étapes pour s'inscrire ou se connecter. Cette simplicité ne se fait pas au détriment de la sécurité, puisque j'ai mis en place des mesures robustes pour protéger l'accès aux comptes.
De plus, centraliser ce service me permet de lier mes projets entre eux. Si un utilisateur se connecte à Econoris avec son compte FlorAccess, il pourra accéder instantanément à mes autres applications compatibles avec les mêmes identifiants. Cela rend l'expérience utilisateur beaucoup plus fluide. À terme, les applications pourront également interagir entre elles pour proposer des fonctionnalités transversales très utiles.
Le projet FlorAccess se divise en plusieurs composants distincts : la base de données, l'API d'authentification, l'intégration applicative et l'application d'administration.
Tout d'abord, j'ai conçu la base de données pour stocker les informations des utilisateurs. J'ai choisi PostgreSQL, un système de gestion de base de données relationnelle open source réputé pour sa fiabilité et ses performances, avec lequel j'avais déjà travaillé.
J'ai pris la décision de stocker uniquement les données personnelles essentielles, à savoir l'email et le pseudo. L'objectif est double : simplifier la phase d'inscription et limiter la quantité d'informations sensibles stockées afin de réduire drastiquement l'impact en cas de fuite de données.
Je pense que les utilisateurs (moi le premier) sont lassés des formulaires d'inscription interminables demandant un excès d'informations privées. En restreignant les champs requis, je rends l'expérience plus rapide, tout en démontrant un respect strict de la vie privée.
Passons en revue la structure de la table users que j'ai mise en place. Chaque colonne possède un rôle bien précis dans le processus d'authentification.
-- Table to store user informationCREATE TABLE users ( id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, email VARCHAR(320) NOT NULL, pseudo VARCHAR(255) NOT NULL,
auth_methods_id INTEGER NOT NULL, is_connected BOOLEAN DEFAULT TRUE, last_logout_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, is_verified_email BOOLEAN DEFAULT FALSE, last_login TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, last_ip VARCHAR(512),
email_verify_token_hash VARCHAR(1024), secret_hash VARCHAR(1024), token_hash VARCHAR(1024),
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT users_email_key UNIQUE (email), CONSTRAINT auth_methods_id_fkey FOREIGN KEY (auth_methods_id) REFERENCES auth_methods(id) ON DELETE RESTRICT);Id : Il s'agit de la clé primaire de la table, générée automatiquement lors de l'inscription. Elle identifie de manière unique chaque utilisateur. Par souci de simplicité, j'ai privilégié un identifiant incrémental classique plutôt qu'un UUID (Universally Unique Identifier).
Email et Pseudo : Ce sont les seules données collectées. L'adresse email sert d'identifiant pour la connexion et la communication, tandis que le pseudo permet de personnaliser l'interface des applications. Une contrainte d'unicité est appliquée sur l'email pour éviter les doublons.
auth_methods_id : Lors de la conception de FlorAccess, j'avais envisagé d'intégrer plusieurs méthodes d'authentification (par mot de passe, par clé asymétrique, ou via des applications tierces comme Google Authenticator). Cette colonne est une clé étrangère pointant vers une table de configuration, ce qui offre une grande évolutivité.
J'ai ainsi développé un système d'authentification flexible qui repose sur un mécanisme de secret à double étape. Quelle que soit la méthode finale, l'utilisateur initie la demande avec son email et reçoit un jeton temporaire en retour de requête. Il doit ensuite valider l'opération via une seconde requête transmettant ce jeton accompagné d'un secret envoyé par un canal distinct (ex. par email) afin de prouver qu'il est bien le propriétaire du compte.
Ce concept permet d'abstraire la nature du secret (code à usage unique, mot de passe classique, jeton applicatif). C'est une architecture simple, modulaire et hautement sécurisée. Bien que l'API soit pensée pour gérer plusieurs canaux, je n'ai implémenté pour le moment que l'envoi de code unique par email, car cette méthode est totalement gratuite, universelle et exempte de dépendances tierces.
is_connected et last_logout_at : Ces deux colonnes sont cruciales. FlorAccess s'appuie sur les JWT (JSON Web Tokens) pour authentifier les requêtes de manière asynchrone. Un JWT est signé numériquement par le serveur et stocké côté client (par exemple dans le localStorage). Par nature, un JWT classique ne peut pas être invalidé avant sa date d'expiration sans une gestion d'état sur le serveur.
C'est là que réside l'ingéniosité de ces deux colonnes : lorsque l'utilisateur demande une déconnexion, la colonne is_connected passe à false et la date courante est enregistrée dans last_logout_at. Lors de chaque appel API, le serveur vérifie la validité du JWT, puis s'assure que le champ is_connected est bien à true et que la date d'émission du token est postérieure à last_logout_at. Si ce n'est pas le cas, la requête est instantanément rejetée.
Ce mécanisme comble l'une des faiblesses majeures des architectures JWT traditionnelles. En cas de vol de jeton, l'utilisateur peut forcer l'invalidation de tous ses accès actifs simplement en se déconnectant, ce qui apporte un excellent niveau de sécurité applicative. Cela permet également de proposer une fonctionnalité native de déconnexion globale sur tous les appareils simultanément.
is_verified_email, last_login et last_ip : Ces champs permettent d'auditer l'activité des comptes. Conserver une trace de l'adresse IP et de l'horodatage permet de détecter des comportements anormaux (comme une tentative d'accès simultanée depuis deux pays différents) et d'alerter l'utilisateur en cas de suspicion de compromission. À terme, ces données permettront de bloquer automatiquement des requêtes malveillantes.
email_verify_token_hash, secret_hash, token_hash : Pour des raisons de sécurité évidentes, aucune donnée d'authentification n'est stockée en clair. J'utilise des mécanismes de hachage cryptographique (fonctions à sens unique). Même si la base de données venait à être compromise, les attaquants ne se retrouveraient qu'avec des chaînes de caractères mathématiquement impossibles à inverser, protégeant ainsi l'accès aux comptes.
Une fois la base de données modélisée, j'ai développé le cœur de l'API. Elle est construite autour de l'environnement NodeJS en utilisant TypeScript, le framework Express, la bibliothèque de validation Zod et l'outil de documentation Swagger. J'utilise également la bibliothèque standard jsonwebtoken pour l'émission des jetons.
Ces technologies correspondent aux standards industriels que j'utilise chez Qowisio. Mon maître d'apprentissage m'a conforté dans ce choix, notamment en raison de la gestion native et performante de l'asynchronisme par NodeJS, ce qui s'avère parfait pour absorber un grand nombre de requêtes simultanées sans bloquer le thread principal.
Analysons le rôle de chacun de ces outils dans le projet :
NodeJS permet d'exécuter du code JavaScript côté serveur en s'appuyant sur le moteur ultra-rapide V8 de Google. Son architecture événementielle non bloquante est idéale pour les API. De plus, utiliser un langage unique pour le frontend et le backend simplifie grandement le flux de développement. Pour FlorAccess, il m'a permis de monter un serveur performant très rapidement, en évitant la lourdeur de configuration d'outils comme Apache ou Nginx en amont.
Cependant, je n'apprécie pas le JavaScript pur en raison de sa permissivité excessive. L'absence de typage statique complexifie la maintenance à grande échelle et est une source fréquente de bugs en production. C'est pourquoi j'utilise systématiquement TypeScript. Ce sur-ensemble compile le code en JavaScript tout en appliquant une vérification stricte des types lors de la phase de build, garantissant la robustesse de l'application.
Le framework Express structure la logique réseau en prenant en charge le routage HTTP (points d'entrée de l'API) et la gestion des requêtes/réponses. C'est un outil minimaliste, rapide et extrêmement populaire au sein de la communauté.
Couplé à Express, Zod s'occupe de la validation des données. Le concept de Zod consiste à définir des schémas stricts décrivant la structure attendue des données entrantes. TypeScript en déduit automatiquement les types associés, ce qui unifie la validation à l'exécution et le typage à la compilation.
Si les données transmises par un client sont conformes au schéma Zod, la requête se poursuit ; sinon, une erreur explicite est immédiatement retournée. Les schémas permettent également de normaliser les données (par exemple, forcer les adresses email en minuscules et retirer les espaces superflus). Grâce à Zod, j'ai la certitude qu'aucune donnée corrompue ou invalide ne pénètre dans la logique métier de l'API, ce qui supprime une quantité considérable de bugs potentiels.
Voici le schéma Zod mis en place pour l'inscription des utilisateurs :
/** * Schéma de validation pour un email, avec prétraitement pour normaliser l'email. */export const EmailSchema = z.preprocess( (val) => typeof val === "string" ? val.toLowerCase().replace(/s+/g, "") : val, z.email());
/** * Schéma de validation pour un pseudo. */export const PseudoSchema = z.string().trim().min(3).max(255);
/** * Schéma de validation pour l'insertion d'un utilisateur. */export const UserInsertSchema = z.object({ email: EmailSchema, pseudo: PseudoSchema,});Une fois les routes sécurisées par ces schémas, il devenait crucial de documenter les points d'entrée de l'API afin de faciliter son intégration future. J'ai choisi Swagger (OpenAPI). Initialement sceptique face à la verbosité de l'outil, j'ai rapidement constaté les bénéfices majeurs de cette approche.
La documentation s'écrit sous forme de commentaires structurés directement au-dessus des routes correspondantes. Elle est donc toujours à jour et synchronisée avec le code. Swagger génère ensuite une interface web intuitive répertoriant toutes les routes par catégorie, avec un code couleur par méthode HTTP (GET, POST, etc.) et un indicateur visuel (un cadenas) signalant si la route requiert une authentification.
L'interface web génère également des exemples précis de requêtes et de réponses pour chaque scénario d'erreur ou de succès.
La fonctionnalité la plus puissante reste le bouton "Try it out". Il transforme la documentation en un client HTTP interactif. Je peux tester mes points d'entrée directement depuis mon navigateur sans avoir à ouvrir un outil externe comme Postman ou Insomnia. Pour exécuter ces requêtes, Swagger génère automatiquement la commande cURL équivalente, ce qui s'avère extrêmement pratique pour déboguer rapidement ou copier la requête dans un terminal.
L'organisation du code est le pilier de la maintenabilité d'un projet. Pour FlorAccess, j'ai mis en place une architecture orientée fonctionnalités (feature-based architecture). Contrairement à une approche traditionnelle en couches (qui sépare tous les contrôleurs d'un côté et tous les modèles de l'autre), l'architecture feature-based regroupe l'intégralité des fichiers nécessaires à une même brique fonctionnelle dans un dossier unique.
Cette approche rend le projet hautement modulaire, limite le couplage technique et simplifie le repérage du code. Si je dois modifier la logique liée à l'inscription, je sais que toute l'action se concentre exclusivement dans le module concerné.
FlorAccess-api├── public│ ├── favicon.ico│ └── favicon.png├── src│ ├── app.ts│ ├── config│ │ └── AppConfig.ts│ ├── core│ │ ├── email│ │ │ ├── error.email.ts│ │ │ ├── mailer.ts│ │ │ └── mail_template.ts│ │ ├── middlewares│ │ │ ├── default_route.middleware.ts│ │ │ ├── error.middleware.ts│ │ │ ├── helmet_http_headers.middleware.ts│ │ │ ├── rate_limiter.middleware.ts│ │ │ └── validators│ │ │ ├── auth_validator.middleware.ts│ │ │ └── request_validator.middleware.ts│ │ ├── models│ │ │ ├── AppError.model.ts│ │ │ └── Database.model.ts│ │ └── utils│ │ └── logger.ts│ ├── modules│ │ ├── admins│ │ │ ├── admins.controller.ts│ │ │ ├── admins.repository.ts│ │ │ ├── admins.routes.ts│ │ │ ├── admins.schema.ts│ │ │ ├── admins.service.ts│ │ │ ├── admins.swagger.ts│ │ │ └── admins.types.ts│ │ └── users│ │ ├── users.controller.ts│ │ ├── users.repository.ts│ │ ├── users.routes.ts│ │ ├── users.schema.ts│ │ ├── users.service.ts│ │ ├── users.swagger.ts│ │ └── users.types.ts│ ├── server.ts│ └── swagger│ └── swagger.ts├── tests│ ├── modules│ │ └── users│ │ └── users.spec.ts│ └── setup.ts├── docker-compose.dev.yml├── docker-compose.yml├── Dockerfile├── tsconfig.json├── jest.config.ts├── package.json├── package-lock.json└── README.mdPour en savoir plus sur l'implémentation de cette structure, vous pouvez consulter ma documentation technique complète sur le sujet.
Si l'on analyse le répertoire principal src/modules, on y trouve deux grands ensembles : users et admins. Le module users englobe la gestion des profils (inscription, connexion, déconnexion) tandis que le module admins regroupe les outils de supervision (recherche globale, envoi de notifications).
Chaque dossier de fonctionnalité reproduit une structure de fichiers normalisée :
*.schema.ts : Contient les règles de validation Zod.*.types.ts : Exporte les types statiques TypeScript déduites des schémas.*.swagger.ts : Traduit les schémas Zod pour l'interface de documentation Swagger.*.routes.ts : Déclare les points d'accès HTTP auprès d'Express et embarque les commentaires Swagger de la route. Les requêtes y passent par un middleware commun de validation (request_validator.middleware.ts) afin de filtrer les données en amont.*.controller.ts : Réceptionne les données préalablement validées par le middleware et orchestre la réponse HTTP finale en fonction des retours de la couche service.*.service.ts : Contient exclusivement la logique métier de l'application (calculs cryptographiques, génération des jetons, règles de gestion). C'est le cerveau du module, totalement isolé de la logique réseau Express.*.repository.ts : Gère les accès aux données. Dans FlorAccess, ce fichier exécute les requêtes SQL vers la base de données PostgreSQL. Isoler cette couche permet de changer de système de stockage (passer à une autre API ou à une base NoSQL) sans jamais impacter la logique métier du service.Une fois l'API stabilisée, l'objectif était de la connecter à mes autres projets afin de centraliser l'authentification. Actuellement, le code d'intégration est dupliqué par copier-coller dans chaque nouvelle application. Pour optimiser cela, j'ai prévu de publier prochainement un package sur le registre NPM pour mes projets Node.js, ainsi qu'une dépendance Flutter, afin de réduire l'intégration à quelques lignes de code.
Prenons l'exemple de l'intégration au sein de l'écosystème de l'application Econoris :
L'application cliente développée avec Flutter recueille l'email de l'utilisateur, interroge FlorAccess pour valider l'accès et réceptionne le JWT, qu'elle stocke localement. Par la suite, pour chaque appel vers le backend d'Econoris, ce jeton est transmis dans les en-têtes HTTP de la requête. Le serveur d'Econoris extrait ce JWT et effectue une requête en arrière-plan vers FlorAccess pour s'assurer de sa validité.
Seul le serveur FlorAccess détient la clé secrète de signature cryptographique nécessaire pour valider le jeton. Si FlorAccess confirme l'authenticité du JWT, le backend d'Econoris traite la demande ; dans le cas contraire, l'accès est immédiatement refusé.
Cette centralisation évite de devoir propager et dupliquer des clés secrètes de signature au sein de multiples serveurs, ce qui réduit drastiquement la surface d'attaque. De plus, cela me permet de modifier la clé secrète de signature périodiquement (rotation des clés) de manière totalement transparente, sans devoir mettre à jour ou redéployer la moindre application cliente.
Pour administrer le système, FlorAccess s'accompagne d'un panneau de contrôle d'administration. Ce client graphique exploite les points d'entrée du module admins pour lister les comptes, analyser les inscriptions et envoyer des emails d'information.
Cette interface a été entièrement codée avec le framework Flutter afin de bénéficier d'une base de code unique et multi-plateforme performante.
L'interface web est entièrement responsive, s'adaptant avec fluidité aux écrans de smartphones, de tablettes ou d'ordinateurs, et intègre un thème clair et un thème sombre pour garantir un excellent confort visuel.
Par mesure de sécurité, il est strictement impossible de promouvoir un utilisateur au rang d'administrateur depuis l'interface ou l'API. Je refuse de laisser une faille logique potentielle permettre une élévation de privilèges non autorisée, ce qui compromettrait l'ensemble de l'infrastructure. Ainsi, la création d'un administrateur se fait exclusivement par une intervention manuelle de ma part, directement via des requêtes en ligne de commande au sein de la base de données PostgreSQL.
À l'avenir, j'envisage d'implémenter un système complet de gestion des rôles et des permissions (RBAC). Néanmoins, à ce stade de mon activité, l'essentiel réside dans le déploiement d'applications fonctionnelles répondant aux besoins de mes premiers utilisateurs.
Ce projet m'a permis d'approfondir mes connaissances sur les rouages de la sécurité et des protocoles d'authentification web. FlorAccess représente désormais une brique logicielle centrale de mon projet d'entreprise de développement d'applications. J'ai pu maîtriser l'architecture de bout en bout et appliquer des technologies modernes de façon professionnelle.
Ce projet valide concrètement mes compétences en développement backend sous l'environnement NodeJS avec TypeScript.
À court terme, je prévois d'ajouter une routine automatisée pour supprimer les comptes inactifs après une longue période d'inactivité.
La priorité absolue reste l'externalisation du code d'intégration sous forme de bibliothèques réutilisables sur NPM et Pub.dev (Flutter). Cela industrialisera mes développements futurs. Dès qu'une mise à jour de sécurité sera poussée sur la bibliothèque, toutes mes applications connectées en bénéficieront instantanément par une simple mise à jour des dépendances. J'attaquerai ces chantiers dès l'obtention de mon diplôme.
À plus long terme, je souhaite diversifier les méthodes de validation en proposing le support optionnel des mots de passe traditionnels et l'intégration de protocoles tiers (OAuth2 via Google ou Facebook). De plus, j'ajouterai des outils de monitoring de sécurité sur le panneau d'administration pour auditer en temps réel les échecs de connexion et bloquer les attaques par force brute.
L'ambition ultime est de propulser FlorAccess comme une alternative open-source, auto-hébergée et hautement personnalisable face aux géants du marché ou aux solutions d'authentification propriétaires payantes, qui imposent des barrières tarifaires ou des contraintes sur la vie privée. FlorAccess combinera la flexibilité des infrastructures cloud modernes et la souveraineté totale d'une solution hébergée chez soi.
FlorAccess est un projet mature, stable et hautement sécurisé. Il propulse déjà mes outils en production et s'est avéré d'une efficacité remarquable pour centraliser la gestion des accès utilisateurs.
Bien qu'il ne soit pas encore parfait et que de nombreux chantiers restent à mener (comme détaillé dans la section "Avenir"), sa version actuelle répond parfaitement au cahier des charges initial. J'ai identifié ses limites techniques et j'ai déjà planifié les correctifs architecturaux pour les prochaines versions, ce qui me rend très confiant quant à l'évolution à long terme de se projet.