Tâche d'authentification
Le problème de l'autorisation dans des dizaines de services a été rencontré il y a quelques années - au début de «l' ère du sciage d'un monolithe ». Ce problème a été résolu avec un nouveau service appelé Auth. Il a aidé à mettre en œuvre une authentification transparente entre divers services et à migrer les données utilisateur vers des bases de données distinctes.
Le service Auth a trois tâches principales:
- Point d'authentification unique (SSO) pour tous les services système . Les services ne stockent pas les informations d'identification, mais les confient à un service dédié.
- Accès sûr et granulaire aux ressources . Sûr car les mots de passe sont stockés au même endroit et sont aussi sécurisés que possible. Granulaire, car les propriétaires de services peuvent configurer l'accès aux ressources comme ils le souhaitent, en fonction des données provenant du service d'authentification.
- . , , .
La première version d'Auth fait partie du monolithe. Il utilise son propre protocole pour communiquer avec les services. Un tel "plan" était nécessaire à ce moment, mais après plusieurs années de travail, des problèmes sont apparus.
Auth fait partie du monolithe . Par conséquent, le service est lié au cycle de publication, ce qui rend impossible le développement et le déploiement indépendants. En outre, vous devrez déployer l'intégralité du monolithe si vous souhaitez déployer Auth, par exemple, lors de la mise à l'échelle d'un service.
Dodo IS dépend de Auth . Dans l'ancienne implémentation, les services externes appellent Auth à chaque action de l'utilisateur pour valider les données le concernant. Cette liaison serrée peut entraîner l'arrêt de l'ensemble du Dodo IS si Auth est bloqué pour une raison quelconque.
L'authentification dépend de Redis... De plus, c'est assez fort - un dysfonctionnement de Redis entraînera la chute d'Auth. Nous utilisons Azure Redis, pour lequel le SLA indiqué est de 99,9%. Cela signifie que le service peut être indisponible jusqu'à 44 minutes par mois. Un tel temps d'arrêt n'est pas autorisé.
L'implémentation actuelle d'Auth utilise son propre protocole d'authentification sans s'appuyer sur des normes . Dans la plupart de nos services, nous utilisons C # (si nous parlons du backend) et nous n'avons aucun problème avec la maintenance de la bibliothèque pour notre protocole. Mais si des services en Python, Go ou Rust apparaissent soudainement, le développement et la prise en charge de bibliothèques pour ces langages prendront du temps supplémentaire et apporteront une complexité supplémentaire.
L'authentification actuelle utilise un schéma de contrôle d'accès basé sur les rôles basé sur les rôles... En général, le rôle bénéficie d'un accès complet à un service spécifique, au lieu d'être lié à une fonctionnalité spécifique. Par exemple, dans les pizzerias, il y a des directeurs adjoints qui peuvent conduire certains projets: établir des échéanciers ou prendre en compte les matières premières. Mais nous n'avons pas la délivrance de droits sur des composants spécifiques du système. Vous devez accorder un accès complet au service afin que les employés puissent accéder à la planification ou aux paramètres de n'importe quel composant comptable.
Des problèmes nous ont incités à concevoir et à écrire une nouvelle version d'Auth. Au début du projet, nous avons passé 3 semaines à étudier les standards d'autorisation et d'authentification OAuth 2.0 et OpenID Connect 1.0.
Remarque... Exagéré, l'article est un récit de la RFC, qui a dû être relue plusieurs fois pour comprendre ce qui se passait autour. Ici, j'ai essayé de sortir de cette complexité et de tout dire de manière simple, structurée, concise et sans décrire des choses complexes, par exemple, quels caractères la réponse du service peut contenir. Contrairement à RFC, après avoir lu ceci une fois, vous pouvez tout comprendre. J'espère que l'article sera utile et fera gagner du temps lors du choix d'une solution pour implémenter un service d'authentification, ou peut-être qu'il fera réfléchir quelqu'un à son besoin.
Qu'est-ce que OAuth2.0?
Nous avons décidé de commencer le développement de la nouvelle Auth en examinant les protocoles et technologies disponibles. La norme d'autorisation la plus courante est le cadre d'autorisation OAuth2.0.
La norme a été adoptée en 2012, et sur 8 ans, le protocole a été modifié et complété. Il y a tellement de RFC que les auteurs du protocole original ont décidé d'écrire OAuth 2.1, qui combinerait toutes les modifications actuelles d'OAuth 2.0 dans un seul document. Alors qu'il est au stade du projet .
La version actuelle d'OAuth est décrite dans la RFC 6749 . Nous allons l'analyser.
OAuth 2.0 est un cadre d'autorisation.
Il décrit comment la communication entre les services doit être mise en œuvre pour garantir une autorisation sécurisée. De nombreuses nuances sont décrites de manière suffisamment détaillée, par exemple le flux d'interaction des nœuds entre eux, mais certaines sont laissées à la merci d'une implémentation spécifique.
Traits:
- Séparer l'entité de l'utilisateur et l'application demandant l'accès . Grâce à cette séparation, nous pouvons gérer les droits applicatifs séparément des droits utilisateurs.
- Au lieu du login et du mot de passe habituels, qui ont un certain ensemble de droits et de durée de vie, nous avons accès aux ressources en utilisant des chaînes générées aléatoirement - des jetons .
- Vous pouvez émettre des droits aussi précisément que possible , sur la base de vos propres souhaits, et non sur un ensemble de droits prédéterminé.
Regardons de plus près les fonctionnalités.
Rôles
OAuth 2.0 définit quatre rôles:
- Le propriétaire de la ressource est une entité qui a des droits d'accès à une ressource protégée. Une entité peut être un utilisateur final ou une sorte de système. Une ressource protégée est un point de terminaison HTTP, qui peut être n'importe quoi: point de terminaison d'API, fichier sur CDN, service Web.
- Serveur de ressources - un serveur qui stocke une ressource protégée à laquelle le propriétaire de la ressource a accès.
- Client . Il s'agit d'une application qui demande l'accès à une ressource protégée au nom du propriétaire de la ressource et avec sa permission - avec autorisation.
- Serveur d'autorisation - un serveur qui émet un jeton à un client pour accéder à une ressource protégée après une autorisation réussie du propriétaire de la ressource.
Chaque participant à l'interaction peut combiner plusieurs rôles. Par exemple, un client peut être propriétaire d'une ressource en même temps et demander l'accès à ses propres ressources. Examinons plus en détail le schéma d'interaction.
Important: le client doit être inscrit au service à l'avance. Comment faire?
Inscription client
Vous choisissez la méthode d'enregistrement du client, par exemple, découverte manuelle ou de service, en fonction du
URI de redirection - l'adresse à laquelle le propriétaire de la ressource sera envoyé après une autorisation réussie. En plus de l'autorisation, l'adresse est utilisée pour confirmer que le service qui a demandé l'autorisation est bien celui qu'il prétend être.
Type de client - le type de client qui détermine la façon dont vous interagissez avec lui. Le type de client est déterminé par sa capacité à stocker en toute sécurité ses informations d'identification pour l'autorisation - un jeton. Par conséquent, il n'y a que 2 types de clients:
- Confidential — , . , web-, backend.
- Public — . , , .
Le jeton dans OAuth 2.0 est une chaîne qui n'est pas transparente pour le client. Habituellement, la chaîne semble avoir été générée aléatoirement - son format n'a pas d'importance pour le client. Un jeton est une clé pour accéder à quelque chose, par exemple, à une ressource protégée (jeton d'accès) ou à un nouveau jeton (jeton d'actualisation).
Chaque jeton a sa propre durée de vie . Mais le jeton d'actualisation devrait en avoir plus, car il est utilisé pour obtenir un jeton d'accès. Par exemple, si la durée de vie d'un jeton d'accès est d'environ une heure, le jeton d'actualisation peut être laissé actif pendant une semaine entière.
Le jeton d'actualisation est facultatif et disponible uniquement pour les clients confidentiels... En utilisant le jeton facultatif, dans certaines implémentations, la durée de vie du jeton d'accès est très longue et le jeton d'actualisation n'est pas du tout utilisé, afin de ne pas se soucier de la mise à jour. Mais ce n'est pas sûr. Si le jeton d'accès a été compromis, il peut être réinitialisé et le service recevra un nouveau jeton d'accès à l'aide du jeton d'actualisation. S'il n'y a pas de jeton d'actualisation, vous devrez recommencer le processus d'autorisation.
Un jeton d'accès se voit attribuer un certain ensemble de droits d'accès, qui est émis au client lors de l'autorisation. Jetons un coup d'œil à ce à quoi ressemblent les autorisations dans OAuth 2.0.
Des droits d'accès
Les droits d'accès sont délivrés au client en tant que périmètre. Scope est un paramètre composé de chaînes séparées par des espaces - scope-token.
Chacun des jetons d'étendue représente des droits spécifiques accordés au client. Par exemple, un jeton d'étendue
doc_read
peut fournir un accès en lecture à un document sur un serveur de ressources et un employee
accès aux fonctionnalités de l'application uniquement pour les employés de l'entreprise. Pourrait ressembler à la portée finale ceci: email doc_read employee
.
Dans OAuth 2.0, nous créons nous-mêmes des jetons d'étendue, en les personnalisant en fonction de nos besoins. Les noms de jetons de portée ne sont limités que par fantaisie et deux caractères ASCII -
"
et \
.
Au stade de l'enregistrement du client, dans les paramètres du service d'autorisation, le client reçoit par défaut une portée standard. Mais le client peut demander au serveur d'autorisation une portée autre que celle standard. Selon les stratégies sur le serveur d'autorisation et le choix du propriétaire de la ressource, la portée résultante peut sembler très différente. À l'avenir, après avoir autorisé le client, le propriétaire de la ressource peut retirer certains droits sans ré-autoriser le service, mais pour émettre des autorisations supplémentaires, une nouvelle autorisation du client sera nécessaire.
Résumé OAuth 2.0. Flux à l'aide d'un jeton d'accès
Nous avons examiné les rôles, les types de jetons et aussi à quoi ressemble la portée. Regardons le flux de fourniture d'accès au service.
Vous trouverez ci-dessous un diagramme abstrait (ou flux) d'interaction entre les participants. Toutes les étapes de ce diagramme sont effectuées strictement de haut en bas. Analysons plus en détail.
- Le client envoie une demande d'accès au propriétaire de la ressource requise.
- Le propriétaire de la ressource redonne au client une autorisation d'autorisation, qui confirme l'identité du propriétaire de la ressource et ses droits sur la ressource à laquelle le client demande l'accès. Selon le flux, cela peut être un jeton ou des informations d'identification.
- Le client envoie l'octroi d'autorisation obtenu à l'étape précédente au serveur d'autorisation, en attendant un jeton d'accès de lui pour accéder à la ressource protégée.
- Le serveur d'autorisation s'assure que l'octroi d'autorisation est valide, puis renvoie le jeton d'accès au client.
- Après avoir reçu le jeton d'accès, le client demande la ressource protégée au serveur de ressources.
- Le serveur de ressources s'assure que le jeton d'accès est correct, puis fournit l'accès à la ressource protégée.
Le client reçoit l'approbation du propriétaire de la ressource, sur la base de laquelle il a accès à la ressource. C'est simple. Sera-ce aussi facile si nous ajoutons un jeton d'actualisation à ce schéma?
Résumé OAuth 2.0. Flux à l'aide du jeton d'actualisation
Les première et deuxième étapes sont omises de ce diagramme - elles ne sont pas différentes du diagramme de flux abstrait ci-dessus.
Schéma plus en détail:
- Le client est livré avec une autorisation d'autorisation au serveur d'autorisation et demande à lui fournir un jeton d'accès et un jeton d'actualisation.
- Authorization server , authorization grant access token refresh token.
- Client access token , — invalid token error.
- , authorization server refresh token access token .
- access token, refresh token, refresh token.
grant?
Grant est une donnée qui représente l'autorisation réussie du client par le propriétaire de la ressource, utilisée par le client pour obtenir un jeton d'accès.
Par exemple, lorsque nous nous authentifions avec Google quelque part, une notification apparaît devant nos yeux. Il indique que tel ou tel service souhaite accéder aux données vous concernant ou à vos ressources (le jeton d'étendue demandé est affiché). Cette notification s'appelle «Écran de consentement».
Au moment où l'on clique sur "OK", la même subvention entre dans la base de données: on enregistre des données indiquant que tel ou tel utilisateur a donné à tel ou tel accès à tel ou tel service. Le client reçoit une sorte d'identificateur d'authentification réussie, comme une chaîne, qui est associée aux données de la base de données.
Il existe 4 + 1 façons d'obtenir une subvention - type de subvention:
- Authorization code — confedencial — web-.
- Client credentials — confedential , , .
- Implicit — public-, redirection URI (, ), authorization code grant PKCE (Proof Key for Code Exchange — , , token , . — RFC 7636).
- Informations d'identification du mot de passe du propriétaire de la ressource . Dans la RFC 6819 de sécurité OAuth 2.0, ce type d'octroi est considéré comme non fiable. Si auparavant, il était autorisé à être utilisé uniquement pour la migration des services vers OAuth 2.0, pour le moment, il n'est pas du tout autorisé à être utilisé.
- Autorisation d'appareil (ajoutée dans la RFC 8628) - utilisée pour autoriser les appareils qui peuvent ne pas avoir de navigateur Web, mais qui peuvent fonctionner sur Internet. Par exemple, il s'agit d'applications de console, d'appareils intelligents ou de téléviseurs intelligents.
Seuls le code d'autorisation (avec PKCE), les informations d'identification du client et l' octroi d'autorisation de l'appareil peuvent être considérés comme pertinents , mais nous considérerons tout. Nous considérerons la subvention par ordre de complexité croissante de compréhension.
Client credentials grant flow
Il a le flux le plus simple, qui rappelle l'autorisation régulière sur n'importe quel service. Elle est effectuée à l'aide des informations d'identification du client, qui sont l'ID client et le secret client - un analogue de l'identifiant et du mot de passe de l'utilisateur. Étant donné que l'authentification nécessite un secret client qui doit être stocké de manière appropriée, ce flux ne peut être utilisé que par des clients confidentiels.
Le schéma est simple: le client est authentifié sur le serveur d'autorisation en passant l'identifiant client et le secret client. En réponse, il reçoit un jeton d'accès, avec lequel il peut déjà accéder au service requis.
Ce flux est requis lorsque le client tente d'accéder à ses propres ressources ou ressources préalablement convenues avec le serveur d'autorisation. Par exemple, le service A doit aller de temps en temps au service B et y mettre à jour les données sur le nombre de pizzerias dans le réseau.
Flux d'informations d'identification du mot de passe du propriétaire de la ressource
Selon les recommandations de sécurité actuelles décrites dans cette RFC , il n'est pas du tout recommandé d'utiliser ce flux en raison de problèmes de sécurité évidents.
Dans l'illustration de ce flux, il y a deux clients et, en théorie, il devrait y avoir un client et un serveur d'autorisation.
Le propriétaire de la ressource transfère son nom d'utilisateur et son mot de passe au client, par exemple, via des formulaires sur le client. Le client, à son tour, l'utilise pour obtenir un jeton d'accès (et, éventuellement, un jeton d'actualisation).
Il y a un problème ici. Le propriétaire de la ressource prend simplement et donne sous forme claire son nom d'utilisateur et son mot de passe au client, ce qui n'est pas sûr. Il a été conçu à l'origine uniquement pour les clients en qui vous avez confiance ou ceux qui font partie du système d'exploitation. Plus tard, il n'a été autorisé que pour la migration de la connexion et de l'authentification par mot de passe vers OAuth 2.0. Les consignes de sécurité actuelles interdisent son utilisation.
Code d'autorisation
Le flux le plus courant pour le moment. Principalement utilisé pour les clients confidentiels, mais avec l'introduction d'une validation supplémentaire avec PKCE, il peut également être utilisé pour les clients publics.
Dans ce flux, l'interaction entre le client et le propriétaire de la ressource passe par l'agent utilisateur (navigateur). L'agent utilisateur a une exigence: il doit être capable de travailler avec des redirections HTTP. Sans cela, le propriétaire de la ressource ne pourra pas accéder au serveur d'autorisation et revenir avec une autorisation.
Ce flux est plus compliqué que les précédents, nous allons donc l'analyser étape par étape. Pour commencer, imaginons que nous sommes propriétaires de ressources et que nous sommes allés à la page d'un service d'apprentissage en ligne qui souhaite enregistrer les résultats d'apprentissage dans notre cloud. Il doit avoir accès à notre ressource, par exemple, un certain répertoire dans le cloud. Nous cliquons sur "Connexion" et le voyage à travers le flux d'octroi de code d'autorisation commence:
- Dans la première étape, le client redirige le propriétaire de la ressource à l'aide de l'agent utilisateur vers la page d'authentification du serveur d'autorisation. Dans l'URI, il spécifie l'ID client et l'URI de redirection. L'URI de redirection est utilisé pour comprendre où renvoyer le propriétaire de la ressource une fois l'autorisation réussie (le propriétaire de la ressource accordera l'autorisation à l'étendue demandée par le client).
- user-agent, resource owner .
- Resource owner , consent screen .
- Resource owner user-agent URI, redirection URI. query- authorization code — , , resource owner .
- authorization code , access token ( refresh token, ).
- authorization code, , access token ( refresh token). .
Si nous nous imaginons à la place du propriétaire de la ressource, nous voyons simplement une redirection vers le serveur d'autorisation, nous authentifions, confirmons l'accès à l'écran Consentement et nous envoyons vers un service déjà en cours d'exécution. Par exemple, nous traversons cela plusieurs fois lorsque nous allons au service avec un compte Google, Facebook ou Apple.
Le flux suivant s'appuie sur cela.
Subvention implicite
Il s'agit d'une optimisation du flux d'octroi de code d'autorisation pour les clients publics qui savent comment travailler avec les URI de redirection. Par exemple, pour les applications de navigateur JavaScript ou les applications mobiles. L'exigence d'un user-agent, avec lequel le client et le propriétaire de la ressource interagissent, demeure: il doit être capable de travailler avec des redirections HTTP.
Il y a une différence principale entre le code d'autorisation et implicite: au lieu de recevoir un code d'autorisation et un jeton d'accès, nous recevons immédiatement un jeton d'accès après l'autorisation réussie du propriétaire de la ressource. De plus, le secret client n'est pas utilisé ici pour des raisons de sécurité - l'application peut être désassemblée et récupérée. L'authenticité est vérifiée uniquement par l'URI de redirection.
De nombreuses étapes de ce diagramme sont similaires aux étapes du code d'autorisation, mais je propose de les analyser également en détail. Imaginons qu'une application de navigateur veuille enregistrer ses paramètres dans notre référentiel Git. Nous cliquons sur "Connexion à GitHub" et à ce stade, le flux implicite commence:
- Le client utilise l'agent utilisateur et une redirection HTTP pour rediriger le propriétaire de la ressource vers le serveur d'autorisation. Dans les paramètres de demande, il transmet l'ID client et les URI de redirection nécessaires pour authentifier le client, puis renvoyer le propriétaire de la ressource.
- Le propriétaire de la ressource est authentifié en communiquant via l'agent utilisateur avec le serveur d'autorisation. En même temps, il confirme la délivrance d'une subvention au client, avec le numéro de client duquel il est venu.
- grant ( «allow» consent screen), user-agent resource owner redirection URI. , URI fragment access token (URI fragment — , URI ‘#’).
- user-agent. User-agent redirection URI web-, access token . , , , CDN.
- Web- web- ( ), redirection URI, , .
- User-agent , , web-hosted client resource, access token.
- L'agent utilisateur du jeton d'accès résultant est simplement transféré au client.
C'est un flux complexe. Il a peu d'utilité dans les scénarios du monde réel. Mais il peut encore être trouvé dans les projets hérités.
Autorisation de l'appareil (RFC 8628)
De 2012 à 2019, de nombreux appareils intelligents sont apparus qui ne sont pas pratiques pour se connecter. Par exemple, il n'est pas pratique de saisir un nom d'utilisateur et un mot de passe complexes sur le téléviseur chaque fois que vous ouvrez une ressource. Cela n'est pas possible sur certains périphériques, tels que les systèmes d'exploitation serveur sans interface graphique. En août 2019, ce flux est apparu uniquement pour de tels scénarios.
Il y a au moins 3 exigences pour les appareils afin de pouvoir fonctionner avec le flux d'octroi de l'autorisation de l'appareil:
- L'appareil doit pouvoir émettre des requêtes HTTPS sortantes.
- L'appareil doit pouvoir afficher l'URI et l'ID à l'utilisateur.
- Chaque appareil autorisé appartient au propriétaire de la ressource, qui, pour une autorisation réussie, doit disposer d'un autre appareil avec un navigateur pour accéder à l'URI spécifié et entrer le code spécifié.
Peut-être que le schéma semble compliqué en raison de l'abondance de flèches. Analysons-le pas à pas, au fur et à mesure que nous analysions des flux complexes avant lui.
Disons que nous essayons de nous connecter à un service Web à l'aide du téléviseur. Nous voyons le bouton "Connectez-vous en tant que périphérique" et cliquez sur. À ce moment, notre flux d'appareils commence:
- Le téléviseur fait une demande au serveur d'autorisation, en lui donnant son ID client.
- Le serveur d'autorisation vérifie qu'un tel client est enregistré et possède le type d'accord approprié.
- , Authorization server device code, user code verification URI. Device code — , .
- user code verification URI — resource owner. Redirection URI , QR- — .
- , user code verification URI, .
- resource owner. verification URI, user code, , scope . resource owner .
- Pendant tout ce temps, l'appareil (point 3) a interrogé le serveur d'autorisation sur son succès. L'appareil se rend à nouveau au serveur d'autorisation avec son code d'appareil et son ID client dans l'espoir que l'autorisation est passée cette fois.
- Cette fois, lorsque le propriétaire de la ressource a confirmé le transfert des droits nécessaires vers l'appareil, le serveur d'autorisation renvoie un jeton d'accès en réponse à la demande (s'il est fourni par les paramètres du serveur et le jeton d'actualisation). Et avec l'aide du jeton, l'appareil peut déjà continuer à fonctionner avec la ressource.
Malgré l'apparente complexité des flèches, ce flux est également assez simple. Si vous avez besoin d'interagir avec des appareils (et nous en avons beaucoup: tracker, caisse enregistreuse, vitrines et autres appareils), vous devez utiliser ce flux.
Au lieu de la sortie
Dans cet article, j'ai omis de nombreux détails afin de parler des choses les plus importantes de la manière la plus simple et la plus accessible. Par exemple, les types de demandes, comment et sous quelle forme transmettre les paramètres, quels caractères sont autorisés comme valeurs pour cela.
Si vous voulez plonger dans le sujet plus en détail, je recommande dans RFC 6749 (pour OAuth 2.0) et RFC 8628 (pour Device Flow). Vous pouvez également consulter la ressource OAuth pour les RFC à jour .
Si l'article était utile et que vous voulez plus de détails - écrivez dans les commentaires, et dans les prochains articles, je parlerai de PKCE, du protocole d'authentification OpenID Connect 1.0, de notre implémentation du serveur d'authentification et bien plus encore.
Liens utiles: