J'ai vu récemment de nombreux articles suggérant que l'avènement du Web3 et l'utilisation généralisée des portefeuilles conduiront les utilisateurs à abandonner les systèmes de comptes traditionnels basés sur les e-mails/mots de passe, et à la place, à se connecter à l'aide de leur portefeuille.
Pour être honnête, après avoir utilisé quelques dApps, le flux de travail super simple d'un clic ou deux de votre portefeuille qui apparaît est en effet une expérience supérieure.
Beaucoup de ces articles continuent en disant "oui, nous n'avons plus besoin de JWT!".
C'est là que je ne suis pas d'accord.
Les JWT, bien qu'un peu déroutants au début, sont incroyablement utiles pour les ingénieurs backend, en particulier ceux des systèmes de microservices. D'autant plus que ces systèmes - un grand nombre d'entre eux - existent déjà et s'intègrent déjà aux JWT ! Ethereum est génial et tout, mais nous n'avons vraiment pas besoin de réinventer la roue. C'est bien de pouvoir continuer à utiliser les mêmes outils backend auxquels vous êtes habitué quand vous en avez besoin.
La connexion avec MetaMask prouve que c'est bien vous - mais comment prouver aux futurs appels d'API que c'est bien vous ?
La foule "vis-JWT" suggère d'utiliser des identifiants de session simples, mais c'est un pas en arrière par rapport au début des années 2000, remarquez, lorsque les architectures en couches simples étaient la norme, qui n'avaient pas la complexité des systèmes backend d'aujourd'hui.
Malheureusement, les identifiants de session ne peuvent pas être vérifiés sans un aller-retour supplémentaire dans la base de données pour déterminer si l'identifiant de session attribué appartient ou non à une session valide. Cela signifie que lorsque le service backend reçoit la demande contenant l'ID de session, il envoie lui-même une demande au serveur d'authentification en demandant "est-ce actif" - puis tous les autres services du système Microservice demandent la même chose.
S'il y a plusieurs services impliqués, cela pourrait être plusieurs allers-retours supplémentaires vers le service d'authentification.
Afin de remédier à cette situation, les experts en cryptographie ont réfléchi.
Ce qu'ils ont proposé, ce sont les JWT - qui font maintenant partie de la norme OpenID, vous savez, celle que Keycloak, Auth0 et d'autres vous aident à mettre en œuvre.
La solution consistait à accorder un ensemble de jetons - JSON Web Tokens pour être précis. Cet ensemble se compose d'un AccessToken
, d'un RefreshToken
et d'un IdToken
. Ces jetons étaient ensuite « signés » par un secret - généralement appelé ClientSecret
. La signature, juste pour que nous soyons sur la même page, est un algorithme de hachage cryptographique, dans le cas des JWT - généralement HS256
(la valeur par défaut pour Auth0). Dans le cas de HS256
le ClientSecret
est utilisé comme entrée et devient donc la clé nécessaire pour déchiffrer avec succès ce hachage - ou le "vérifier". Avec RS256
et ES256
, une paire de clés publique/privée est utilisée, c'est-à-dire signée par une clé privée et vérifiée avec une clé publique sur le client.
Cela signifie que si un service backend reçoit l'un de ces jetons et qu'il connaît le ClientSecret
, il peut vérifier que le jeton a bien été émis par le service d'authentification qui a signé ce jeton. Le jeton utilisé lors de la tentative d'accès à un service principal est le AccessToken
. Ces jetons contiennent spécifiquement des informations sur qui est l'utilisateur ainsi que ses "revendications", ou en d'autres termes, ce qu'il est autorisé à faire formaté pour le système qui s'en soucie.
Pour les systèmes de microservices, cela signifie que chaque service qui se soucie de vérifier l'identité a juste besoin de connaître le ClientSecret
car il peut vérifier que le JWT est authentique en l'utilisant, plutôt qu'un aller-retour vers la base de données. Dans un système avec de nombreux microservices, cela peut réduire de nombreux allers-retours supplémentaires vers la base de données, ce qui rend l'ensemble du système plus évolutif.
Les jetons d'accès, s'ils sont volés, peuvent être utilisés à des fins malveillantes, et pour cette raison, il est important de prendre les précautions appropriées lors de la conception d'un système pour les utiliser.
L'ensemble minimum de précautions, en plus de signer et de vérifier les jetons, consiste à :
Définir une date d'expiration sur le jeton d'accès JWT de 5 à 15 minutes et s'assurer que les jetons n'ont pas expiré lorsqu'ils sont reçus
Ne stockez pas le jeton d'accès, sauf en mémoire
Émettez des jetons d'actualisation qui peuvent être stockés et envoyés à un serveur pour vérification, et actualisés avec l'utilisation du ClientSecret
et du ClientId
qui l'ont émis.
À utiliser uniquement lors de la transmission via des connexions TLS (HTTPS)
Utilisez des cookies HTTP uniquement afin qu'ils ne puissent pas être modifiés côté navigateur
Paramètres CORS
Une clé de la même taille que la sortie de hachage (par exemple, 256 bits pour "HS256") ou plus DOIT être utilisée avec cet algorithme. - RFC7518 , note : Auth0 utilise 512 bits pour HS256.
Un client confidentiel a besoin d'un serveur intermédiaire, tel que Node.js - ce serveur est un proxy du service d'authentification, de sorte que le client n'a pas besoin de connaître le secret du client. Un client public expose le secret du client et il n'y a pas de serveur proxy entre le navigateur et le service d'authentification. Cela peut être davantage restreint avec les paramètres CORS afin que seules les demandes de certains domaines soient autorisées.
Les précautions supplémentaires incluent :
Et probablement plus - par exemple, si vous détectez quelqu'un essayant d'utiliser un jeton d'actualisation révoqué, vous pouvez révoquer tous les jetons d'actualisation actifs de cet utilisateur.
Certes, toutes ces précautions peuvent rendre la tâche un peu difficile. Il y a beaucoup à comprendre. Il y a aussi des problèmes d'utilisabilité. Un utilisateur se déconnectant de votre site toutes les 5 minutes serait assez ennuyeux pour lui. Pour éviter cela, une boucle d'actualisation silencieuse doit être implémentée dans l'application consommatrice afin d'actualiser en permanence l'ensemble de jetons.
Cela dit, de l'autre côté de la réussite, il est possible de s'intégrer en toute sécurité à tous vos systèmes backend de manière évolutive, ainsi qu'à de nombreux outils existants, tels que Hasura , qui peut générer automatiquement toutes vos API pour vous en fonction de un schéma de base de données Postgres connecté. Ainsi, pouvoir s'intégrer facilement aux outils existants peut faire gagner beaucoup de temps de développement.
Si vous utilisez déjà OpenId, il est probable que vous ayez déjà ces éléments en place. C'est, après tout, une norme d'authentification.
Alors, comment pouvons-nous conserver la commodité d'utiliser les JWT dans un Web3 et nous connecter avec le monde MetaMask ?
Commençons par comprendre le flux d'authentification OpenId utilisé pour le SSO.
Dans le monde web3auth, nous remplaçons la deuxième étape par l'utilisation des paires de clés privées et publiques de votre portefeuille pour signer un défi. La redirection n'est pas nécessaire ou souhaitée.
Vous visitez un site Web et souhaitez vous connecter avec votre compte - vous cliquez sur le bouton de connexion
Vous recevez un défi du serveur d'authentification qui ouvre votre portefeuille en vous demandant de "Signer" le défi. Appuyez sur le signe.
Le serveur d'authentification vérifie votre signature et vous délivre un ensemble de JWT qui sont stockés de manière appropriée et utilisés dans les flux d'arrière-plan d'actualisation silencieuse.
Nous remplaçons simplement le flux de redirection de style SSO par un défi signé par votre portefeuille. Le flux après réception des jetons reste le même qu'avec OpenID. Cela signifie que vous pouvez, par exemple, passer de l'utilisation d'OpenID à l'utilisation de web3auth avec un serveur émetteur JWT, et rien sur l'utilisation de ces jetons après leur octroi n'aurait besoin de changer. Toutes vos intégrations backend existantes avec des outils comme Hasura restent exactement les mêmes.
C'est exactement ce que je veux en tant que développeur Full Cycle. Je ne veux pas réinventer la roue. Je souhaite remplacer OpenID par web3auth et continuer à utiliser tous les outils puissants auxquels je suis habitué.
Malheureusement, je n'ai pas trouvé de serveur web3auth qui ait fait cela ainsi que les précautions de sécurité. J'ai trouvé quelques projets démontrant des techniques dans le processus, mais pas tout le flux de bout en bout.
Alors je me suis mis à construire…
J'ai construit ce serveur d'authentification ici : https://github.com/CloudNativeEntrepreneur/web3auth-service
Et voici l'intégration SvelteKit qui va avec, qui implémente toutes les choses - rafraîchissement silencieux, yada yada - toutes ces choses que j'ai mentionnées ci-dessus : https://github.com/CloudNativeEntrepreneur/sveltekit-web3auth
Bien sûr, s'il devait y avoir un client et un exemple GraphQL, il fallait également un serveur et une base de données GraphQL, j'ai donc également fourni des exemples de cela : https://github.com/CloudNativeEntrepreneur/example-hasura + https : //github.com/CloudNativeEntrepreneur/example-readmodel
Cet exemple utilise l' opérateur Zalando Postgres et SchemaHero , il vous suffit donc de déclarer vos bases de données et de décrire votre schéma en YAML, et Hasura générera automatiquement toutes les API GraphQL dont vous avez besoin. ET, j'ai créé le serveur d'authentification avec Hasura à l'esprit, il a donc les revendications appropriées pour s'intégrer au RBAC et aux autorisations de Hasura, qui sont assez robustes.
Et bien sûr, vous avez besoin d'un endroit pour exécuter tout cela, et donc, un cluster de développement local qui configure tous les outils comme istio, et les opérateurs, et SchemaHero pour vous ! https://github.com/CloudNativeEntrepreneur/local-dev-cluster
Mais qui sait même utiliser tout ça ?
C'est pourquoi j'ai créé ce méta dépôt : https://github.com/CloudNativeEntrepreneur/web3auth-meta
L'utilisation de ce méta-dépôt clonera tous les projets dont vous avez besoin aux bons endroits et les exécutera tous ensemble.
Enfin, pour exécuter tous les projets ensemble, vous avez besoin d'outils installés, et l'installation d'outils est ennuyeuse - j'ai donc créé ce dépôt ici qui les installera tous pour vous ! https://github.com/CloudNativeEntrepreneur/onboard
J'ai également publié sveltekit-web3auth
sur npm, et créé un modèle à partir d'un projet SvelteKit qui l'utilise et a configuré et intégré GraphQL avec l'authentification à une instance Hasura, donc lorsque vous êtes prêt à créer vos propres projets, vous pouvez l'utiliser comme modèle ! https://github.com/CloudNativeEntrepreneur/sveltekit-web3auth-template
Si vous n'êtes pas encore prêt pour le monde de l'authentification Web3, vous pouvez également utiliser https://github.com/CloudNativeEntrepreneur/sveltekit-oidc , qui est préconfiguré pour se connecter à votre cluster de développement local, et une instance Keycloak configurée dans ce. Étant donné que les deux projets émettent des JWT, l'objectif est que le système d'authentification soit interchangeable - utilisez web3auth ou OIDC classique - l'utilisation en amont des jetons est la même.
Maintenant, allez créer des dApps hybrides avec des API GraphQL générées automatiquement et un RBAC robuste, des autorisations et des abonnements et des pages SSR authentifiées et des boucles d'actualisation silencieuses et des jetons d'actualisation rotatifs et d'autres choses !
En conclusion, non, les JWT et la connexion avec Ethereum/metamask ne s'excluent pas mutuellement. En fait, si vous aimez la productivité des développeurs et l'intégration avec les outils existants, je pense que vous vous en sortirez très bien en utilisant JWT ET web3auth.
Acclamations!
Je suis disponible pour consultation! Si vous êtes intéressé par mon aide sur un projet sur lequel vous travaillez, envoyez-moi un message sur Twitter !