Auth moi d'un doute ? C'est sécurisé ta connerie ?
J’étais en train de bricoler du FastAPI pour m’amuser un peu sur mon temps libre, particulièrement avec l’authentification JWT. (https://en.wikipedia.org/wiki/JSON_Web_Token)
C’est sympa et formateur, mais bien que je fasse beaucoup de scripts, je doute un peu de mes capacités à construire un système d’authentification et de gestion de permission auquel je puisse avoir 100% confiance.
Et en vrai, tu ne devrais pas faire toi-même ton système de sécurité et d’authentification.
Si ton système est unique, il n’est pas autant éprouvé qu’une solution utilisée par des milliers de personnes :
- Tu vas passer beaucoup de temps à dev et maintenir un système critique et réinventer la roue
- Tu auras toujours un doute sur la sécurité de ton système
- Tu auras toujours un temps de retard sur ce qui se fait de mieux dans le domaine
Si tu délègues ta sécurité :
- Ce sera le problème de ton Identity Provider qui travaillera à la maintenance et l’évolution de ta sécurité
- Tu dégageras du temps pour ce qui importe vraiment pour ton business, les fonctionnalités métiers de ton produit
- Tu auras des moyens d’audit et d’authentification forte, sans effort
“L’homme et sa sécurité doivent constituer la première préoccupation de toute aventure technologique.” (Albert Einstein)
Une fois n’est pas coutume, on va regarder ce que nous propose GCP en service managé. Tu vas me dire gnagnagna vendors lock-in, bouh c’est pas bien. Pour les gens moins Tech qui ne connaissent pas, l’effet « lock-in » est la dépendance d’un client à un produit ou à une technologie.
Alors Monsieur ou Madame “lock-off”, si tu veux trainer un Keycloak, un ORY Hydra un auth0 ou un autre produit open source, grand bien t’en fasses, ça marche aussi, mais ce n’est pas le sujet d’aujourd’hui :)
Car aujourd’hui on essaye Firebase dans un contexte d’Identity Provider. https://en.wikipedia.org/wiki/Identity_provider
Va donc faire ton VMware ou ton OpenStack quelque part ailleurs et arrête de me casser les pieds. Tu peux aussi payer un bras pour Okta ou une autre solution en SaaS, je ne juge pas, si tu as de gros sous à protéger et de gros sous à dépenser :)
Je pars sur l’idée que je n’ai pas du tout envie de trainer une brique à maintenir quelque part dans un cluster Kube ou un compute, que je n’ai pas de sous pour payer un produit en SaaS super cher.
Je veux de la auth dispo et de la auth performance (les fautes là, c’est exprès) et surtout pas maintenir du code custom quelque part ou un produit open source en Java qui me casse les pieds.
Une application prétexte
On va faire une API toute simple qui va nous servir de prétexte pour faire joujou avec de l’authentification.
Je suis radin comme tu le sais bien, donc on va faire ce qui coûte le moins cher possible, un CloudRun avec une API Gateway, Identity Platform/Firebase et un GCS pour persister mes données comme un gros porc en mode JSON chaussettes sales. En gros je prends un bucket GCS et je stocke mes données dedans au format JSON.
Tu peux juger, ce n’est probablement pas une bonne idée :)
C’est pas cher et je n’ai pas envie d’une base de données, je pourrai mettre un BigQuery, ou un CloudSQL, mais je suis curieux des performances que je peux obtenir avec cette idée. Et si vraiment j’étais dans un cas réel, il ne manquerait pas grand-chose pour passer sur une base NoSQL des familles ou migrer ailleurs.
Mais bon, on s’en balance, c’est juste un jouet, on fait du sale :)
Tous ces produits ont un free tier d’utilisation nous permettant de faire un POC gratuit ou presque, on ne va pas s’en priver.
On simule de cette façon un embryon d’application Serverless, comment ça il est moche mon bébé :)
Exploration des outils
On part sur une authentification basique de chez basique en mode email et mot de passe. Je garde la configuration avec les templates d’email par défaut et le serveur de mail de Google par défaut. C’est quand même bien cool de voir que sans rien faire on aura des emails templates pour la récupération et la vérification de mots de passe. On pourrait le faire nous-même, mais finalement est-ce que j’ai vraiment envie de le faire ?
Je suis tout seul, sur mon temps personnel, avec une vélocité de dev moyenne et j’ai jamais dev ce genre de système avant.
Hop encore une fois, je n’ai pas envie de lire la doc, ça tombe bien j’ai trouvé un snippet de code qui fait de l’auth et qui pourrait correspondre à mon besoin :)
https://www.padok.fr/en/blog/setup-application-gcp
Pour le moment, le fonctionnement est un peu Nebula pour moi.
Ce que j’aimerai faire :
- Avoir un point d’API pour enregistrer des utilisateurs dans Firebase
- Fournir des token JWT à mes utilisateurs pour leur permettre d’utiliser l’API
- Avoir des permissions customisées pour donner des permissions fines à mes utilisateurs
- Utiliser les permissions pour lire et écrire des données propres à mes utilisateurs
Et surtout TUER THANOS, pardon je m’égare, hihi
Voyons voir où le script nous emmène
C’est parti pour essayer de faire fonctionner le bout de script glané sur le net. La seule petite difficulté que j’ai trouvée c’est où trouver les informations d’authentification du script.
On trouve ces informations dans la configuration de l’application dans le projet de la console Firebase. https://console.firebase.google.com/
Directement dans le snippet JavaScript, mais nous on va pas utiliser de Js parce que le front, ça ne nous intéresse pas cette fois-ci :)
Il faudra veiller à exporter la clef du Service Account dans une variable d’environnement pour faire fonctionner le script. Et remplacer les infos de connexions à Firebase par celle de votre application.
|
|
|
|
Et donc là, on a une authentification qui fonctionne au strict minimum avec API Gateway, Firestore, Cloudrun. Sur un point d’api hello avec 1 utilisateur.
C’est un bon début :)
FastAPI Cloudrun
Bon maintenant qu’on a un bout de script pour enregistrer les utilisateurs et filer un token, c’est peut-être bien de faire rentrer ça dans un cloudrun, pour manager les utilisateurs via l’api directement.
De cette façon on donne un moyen d’enregistrer et d’authentifier les utilisateurs directement via FastAPI. Et on peut s’authentifier avec JWT sur tous les points d’accès API de la Gateway.
code minimum fastAPI
À partir de maintenant il faut mettre en place de quoi créer des containers fastAPI cloudrun les pousser sur la Registry google et les déployer si possible avec une CI/CD.
Comme d’hab, radin malin, git hook, tequila, heineken pas le temps de niaser !
|
|
Rapidement, j’utilise gcloud builder pour builder mon image docker et pusher sur le registry de mon projet. Et ensuite je passe une variable Terraform pour déployer mon image Cloudrun avec Terraform.
En tentant de fusionner le code FastAPI et le script Firebase, je me rends compte que la dépendance pyrebase est vraiment relou et provoque des incompatibilités de dépendance avec la version de requests utilisé par FastAPI aussi.
Tout ça pour un pauvre wrapper d’API qui m’économise 4 lignes de code, mais qui m’importe quantité de code inutile et dépendances.
On fouille de code de pyrebase et on trouve ça.
https://github.com/thisbejim/Pyrebase/blob/master/pyrebase/pyrebase.py
|
|
On dégage le p’tit wrap, et on prend un big mac
FastAPI authentification + persistance GCS
Au final ça nous fait un truc comme ça
modèle : fichier app/model.py
|
|
Rien de surprenant ou de magique, c’est juste des modèles pydantic tout simples pour gérer mes formulaires utilisateur et Blog. J’ai gardé juste le strict minimum au niveau des champs dans un but de simplicité.
fichier app/main.py
|
|
Donc on a un système minimum viable qui sait enregistrer des utilisateurs, enregistrer des articles de blog de façon rudimentaire, récupérer les articles de l’utilisateur authentifié. On peut aussi changer de mot de passe et retourner des tokens JWT utilisables sur l’API Gateway de GCP et sur notre FastAPI.
On peut gérer les permissions en ajoutant l’information dans le token comme fait dans le code, GCP ne le recommande pas, je dirais que par principe moi non plus (il vaut mieux stocker l’info en base) :)
Pour récupérer les infos utilisateurs depuis le token on doit faire appel à l’API sign_in_with_custom_token. On peut ensuite identifier l’utilisateur par son id et sa permission (ajouté dans le custom token).
J’ai aussi ajouté la persistance dans GCS dans un bucket zonal pour la latence, et je suis surpris par la vitesse de mon bricolage. C’est presque bien, à condition d’organiser les fichiers dans le bucket et de ne pas charger des blobs énormes. https://en.wikipedia.org/wiki/Binary_large_object
Je vous recommande quand même une base de données qui répond bien mieux au besoin, mais si vous voulez vous en passer c’est aussi possible comme ça :)
Finalement on peut dégager la Gateway et la Cloud Function hello.
On a juste besoin du cloudrun, d’une image Docker et des variables d’env.
Le dockerFile
|
|
Le Terraform qui va bien, avec les permissions pour permettre d’écrire des token, utiliser l’API admin de Firebase et lire et écrire dans un bucket GCS.
|
|
Rien de compliqué ou de magique, j’ai mis des variables pour l’image Docker, le tokens Firebase et le bucket GCS. On pourrait utiliser des secrets Cloudrun pour faire plus propre, mais comme on fait du rapidos crados j’ai mis de côté cette idée :)
Backup et Réversibilité
Bon maintenant qu’on à un système qui fonctionne, On va faire en sorte de dumper la base d’utilisateurs Firebase vers un bucket tous les soirs.
En cas de destruction accidentelle et/ou de besoin de réversibilité. J’ai dit qu’on s’en foutait, mais c’est quand même important d’y penser si vous l’utilisez sur un vrai produit. Au moins, pouvoir se garder l’option si Firebase venait à disparaitre ou que vous voulez bouger de GCP.
un Cronjob dans Cloudrun avec Cloud Scheduler devrait faire le taf :)
On tente un dump local pour commencer https://firebase.google.com/docs/cli/auth
installation du cli
curl -sL https://firebase.tools | bash
J’avais pour idée de prendre le même Service Account que le Cloudrun FastAPI. Comme il faut une permission Editor pour avoir le droit de backup des passwords hash, on va refaire un Service Account juste pour les backups avec la permission super pétée, comme captain Marvel.
Il est un peu dangereux d’exposer un service web avec un Service Account avec un des droits les plus forts sur GCP :) Il faudra aussi veiller à restreindre au maximum l’accès au bucket GCS qui contiendra les backups pour éviter une fuite de données ou une utilisation malveillante du Service Account.
Petite subtilité un peu relou, il faut créer le Service Account à la main, comme on utilise des permissions pétées qui normalement s’appliquent uniquement aux utilisateurs humains admin.
|
|
Ici le script qu’on met dans un cloudrun, qui fera les backups Firebase vers un bucket sécurisé. Il faudra brancher un cloud scheduler pour déclencher les backups à un interval régulier.
|
|
Le Dockerfile ici, pas vraiment de trucs magiques non plus :)
Rien dans les poches, rien dans le git, prestidigitation !
|
|
Et du coup pour importer dans l’autre sens il suffit de récupérer le fichier de backup et lancer la commande d’import comme dans la doc.
Conclusion
Globalement Firebase fonctionne plutôt bien pour mon petit bricolage, mais je pense que ça ne suffit pas si on a des besoins beaucoup plus avancés. Néanmoins c’est très pratique si on part sur une architecture Serverless et qu’on ne veut pas gérer seul l’authentification. C’est plutôt rapide, mais pas complètement sans douleur, je ne peux pas dire que je n’ai pas un peu galéré ! Par contre j’ai grandement réduit le code lié à l’authentification et la gestion de mes utilisateurs, en comparaison avec la solution incluse dans FastAPI.
C’est vraiment bien, parce que je m’inquiète moins niveau sécurité et bugs, et le stockage des identifiants de mes utilisateurs.
Quelques possibilités d’amélioration de cette solution :
- Meilleure gestion et révocation de la validité des tokens.
- Meilleure gestion des permissions
- Mettre en place un frontend sympa
- Mettre en place un backend admin
- Distribuer le monolithe FastAPI en microservice
Bien sûr, le code est loin d’être impeccable et prod ready, mais vous avez l’idée générale de comment intégrer Firebase auth dans FastAPI. Avec un peu plus de soins et des tests, je pense que c’est une solution sérieuse et envisageable en production. Le backup et l’import des données sont plutôt simples, mais je déplore le besoin d’être Editor pour avoir le backup complet des Utilisateurs. Je pense que Google pourrait faire un effort pour restreindre les droits nécessaires pour cette action.
J’imagine que comme Firebase n’est pas un produit GCP natif, Google doit faire un effort d’assimilation du produit avant de pouvoir envisager de faire des changements profonds. On peut s’en rendre compte parfois, quand on a des erreurs un peu vagues et moins standard que les produits Google natifs. https://en.wikipedia.org/wiki/Firebase
Je vous laisse donc matière à réflexion et je vous donne rendez-vous bientôt pour une prochaine connerie :)
ici le code fastAPI, Firebase, GCS , déposé comme une machine à laver en fin de vie dans une décharge publique.
https://github.com/helldrum/fastapi-firebase