Réinitialisation du mot de passe RESTful

107

Quelle est la bonne façon de structurer une ressource RESTful pour réinitialiser un mot de passe?

Cette ressource est destinée à être une réinitialisation de mot de passe pour quelqu'un qui a perdu ou oublié son mot de passe. Il invalide leur ancien mot de passe et leur envoie un mot de passe par e-mail.

Les deux options que j'ai sont:

POST /reset_password/{user_name}

ou...

POST /reset_password
   -Username passed through request body

Je suis presque sûr que la demande doit être un POST. Je suis moins sûr d'avoir choisi un nom approprié. Et je ne sais pas si le nom_utilisateur doit être transmis via l'URL ou le corps de la requête.

Chris Dutrow
la source

Réponses:

54

MISE À JOUR: (suite à commenter ci-dessous)

J'irais pour quelque chose comme ça:

POST /users/:user_id/reset_password

Vous avez une collection d'utilisateurs, où l'utilisateur unique est spécifié par le {user_name}. Vous spécifiez alors l'action sur laquelle opérer, ce qui est dans ce cas reset_password. C'est comme dire "Créer ( POST) une nouvelle reset_passwordaction pour {user_name}".


Réponse précédente:

J'irais pour quelque chose comme ça:

PUT /users/:user_id/attributes/password
    -- The "current password" and the "new password" passed through the body

Vous auriez deux collections, une collection d'utilisateurs et une collection d'attributs pour chaque utilisateur. L'utilisateur est spécifié par le :user_idet l'attribut est spécifié par password. L' PUTopération met à jour le membre adressé de la collection.

Daniel Vassallo
la source
10
Je suis d'accord avec votre solution (POST) mise à jour. Les requêtes PUT doivent être idempotentes (c'est-à-dire que les requêtes répétées ne doivent pas affecter le résultat). Ce n'est pas le cas des requêtes POST.
Ross McFarlane
16
Je changerais reset_password en password_reset
Richard Knop
9
Accrochez-vous les gars ... cela ne permettrait-il essentiellement à personne de réinitialiser le mot de passe de quelqu'un? Comme, si c'est pour quelqu'un qui oublie le mot de passe actuel, l'utilisateur concerné ne peut pas être authentifié avec le mot de passe actuel. Donc, essentiellement, cela signifie que cette API ne pouvait accepter aucun mot de passe - permettant ainsi à quiconque de réinitialiser le mot de passe de quelqu'un, et si l'API le renvoie, même obtenir le mot de passe de tout utilisateur connu ??? Ou est-ce que je manque quelque chose
transient_loop
39
Le problème avec / user / {id} / password et autres est que vous ne connaissez peut-être pas l '"id" de l'utilisateur. Vous connaissez leur "nom d'utilisateur", leur "e-mail" ou leur "téléphone", mais pas leur "identifiant".
coolaj86
17
Le défaut fondamental de cette approche est qu'elle suppose que vous connaissez déjà l'identifiant de l'utilisateur. Cela sera vrai dans certaines circonstances, mais comment le faire alors lorsque le nom d'utilisateur ou l'ID utilisateur n'est pas connu si l'utilisateur a seulement besoin de spécifier un e-mail pour la réinitialisation.
Alappin le
95

Utilisateurs non authentifiés

Nous faisons une PUTdemande sur un api/v1/account/passwordpoint de terminaison et avons besoin d'un paramètre avec l'e-mail de compte correspondant pour identifier le compte pour lequel l'utilisateur souhaite réinitialiser (mettre à jour) le mot de passe:

PUT : /api/v1/account/password?email={[email protected]}

Remarque: comme @DougDomeny l'a mentionné dans son commentaire, transmettre l'e-mail en tant que chaîne de requête dans l'url est un risque de sécurité. Les paramètres GET ne sont pas exposés lors de l'utilisation https(et vous devez toujours utiliser une httpsconnexion appropriée pour de telles demandes), mais d'autres risques de sécurité sont impliqués. Vous pouvez en savoir plus sur ce sujet dans cet article de blog ici .

Passer l'e-mail dans le corps de la requête serait une alternative plus sûre que de le transmettre en tant que paramètre GET:

PUT : /api/v1/account/password

Demander le corps:

{
    "email": "[email protected]"
}

La réponse a une signification de réponse 202acceptée :

La demande a été acceptée pour traitement, mais le traitement n'est pas terminé. La demande peut éventuellement être traitée ou non, car elle peut être refusée lorsque le traitement a effectivement lieu. Il n'y a aucune possibilité de renvoyer un code d'état à partir d'une opération asynchrone comme celle-ci.

L'utilisateur recevra un e-mail à [email protected]et le traitement de la demande de mise à jour dépend des actions entreprises avec le lien de l'e-mail.

https://example.com/password-reset?token=1234567890

L'ouverture du lien à partir de cet e-mail dirigera vers un formulaire de réinitialisation de mot de passe sur l'application frontale qui utilise le jeton de réinitialisation du mot de passe du lien comme entrée pour un champ de saisie masqué (le jeton fait partie du lien en tant que chaîne de requête). Un autre champ de saisie permet à l'utilisateur de définir un nouveau mot de passe. Une deuxième entrée pour confirmer le nouveau mot de passe sera utilisée pour la validation sur le front-end (pour éviter les fautes de frappe).

Remarque: dans l'e-mail, nous pourrions également mentionner que si l'utilisateur n'a initialisé aucune réinitialisation de mot de passe, il peut ignorer l'e-mail et continuer à utiliser l'application normalement avec son mot de passe actuel.

Lorsque le formulaire est soumis avec le nouveau mot de passe et le jeton comme entrées, le processus de réinitialisation du mot de passe aura lieu. Les données du formulaire seront à nouveau envoyées avec une PUTdemande, mais cette fois avec le jeton et nous remplacerons le mot de passe de la ressource par une nouvelle valeur:

PUT : /api/v1/account/password

Demander le corps:

{
    "token":"1234567890",
    "new":"password"
}

La réponse sera une réponse 204sans contenu

Le serveur a répondu à la demande mais n'a pas besoin de renvoyer un corps d'entité et peut souhaiter renvoyer des méta-informations mises à jour. La réponse PEUT inclure des méta-informations nouvelles ou mises à jour sous la forme d'en-têtes d'entité, qui, s'ils sont présents, DEVRAIENT être associés à la variante demandée.

Utilisateurs authentifiés

Pour les utilisateurs authentifiés qui souhaitent modifier leur mot de passe, la PUTdemande peut être effectuée immédiatement sans l'e-mail (le compte pour lequel nous mettons à jour le mot de passe est connu du serveur). Dans ce cas, le formulaire soumettra deux champs:

PUT : /api/v1/account/password

Demander le corps:

{
    "old":"password",
    "new":"password"
}
Se flétrir
la source
Dans votre premier paragraphe, vous dites PUT mais l'exemple ci-dessous dit DELETE. Qu'est-ce qui est exact?
jpierson
Cela expose l'adresse e-mail sur l'URL, ce qui serait plus sécurisé pour les données JSON.
Doug Domeny
@DougDomeny Oui, envoyer l'e-mail sous forme de données json serait probablement mieux. J'ai ajouté cela à la réponse en tant qu'option alternative plus sécurisée, sinon la solution peut être la même.
Wilt
@Wilt: Ne serait-ce pas une opération PATCH? PUT nécessite l'envoi de la ressource complète
j10
1
@jitenshah Bon point. En écrivant ceci, je pensais que PUT serait mieux, mais je ne me souviens pas exactement pourquoi. Je suis d'accord avec votre raisonnement selon lequel le patch pourrait être plus adapté à ce cas.
Wilt
18

Soyons uber-RESTful pendant une seconde. Pourquoi ne pas utiliser l'action DELETE pour le mot de passe pour déclencher une réinitialisation? Cela a du sens, n'est-ce pas? Après tout, vous supprimez effectivement le mot de passe existant au profit d'un autre.

Cela signifie que vous feriez:

DELETE /users/{user_name}/password

Maintenant, deux grandes mises en garde:

  1. HTTP DELETE est censé être idempotent (un mot sophistiqué pour dire "pas grand chose si vous le faites plusieurs fois"). Si vous faites des choses standard comme l'envoi d'un e-mail de "réinitialisation du mot de passe", vous allez rencontrer des problèmes. Vous pouvez contourner ce marquage de l'utilisateur / mot de passe avec un indicateur booléen «Est réinitialisé». À chaque suppression, vous vérifiez cet indicateur; si ce n'est pas réglé alors vous pouvez réinitialiser le mot de passe et envoyer votre e - mail. (Notez que cet indicateur peut avoir d'autres utilisations.)

  2. Vous ne pouvez pas utiliser HTTP DELETE via un formulaire , vous devrez donc faire un appel AJAX et / ou tunneler DELETE via le POST.

Craig Walker
la source
9
Idée intéressante. Cependant, je ne vois pas DELETEbien s'intégrer ici. Vous remplaceriez le mot de passe par un mot de passe généré au hasard, je suppose, ce qui DELETEpourrait être trompeur. Je préfère le Create (POST) new reset_password action, où le nom (ressource) sur lequel vous agiriez est "l'action reset_password". Cela convient également à l'envoi d'e-mails, car l'action encapsule tous ces détails de niveau inférieur. POSTn'est pas idempotent.
Daniel Vassallo
J'aime la proposition. Le problème 1 pourrait être traité en utilisant des requêtes conditionnelles, c'est-à-dire HEAD qui envoie ETag + DELETE et If-Match en-tête. Si quelqu'un essaie de supprimer un mot de passe qui n'est plus actif, il obtiendra un 412.
whiskeysierra
1
J'éviterais DELETE. Vous mettez à jour, car la même entité / concept obtiendra une nouvelle valeur. Mais en fait, généralement, cela ne se produit même pas maintenant, mais seulement après l'envoi du nouveau mot de passe dans une demande différente ultérieure (après un e-mail de réinitialisation du mot de passe) - De nos jours, personne n'envoie un nouveau mot de passe par courrier, mais un jeton pour le réinitialiser dans une nouvelle demande avec un jeton donné, non?
antonio.fornie
11
Que faire si l'utilisateur se souvient de son mot de passe juste après avoir effectué une demande de réinitialisation? Qu'en est-il d'un robot essayant de réinitialiser des comptes aléatoires? L'utilisateur doit être autorisé à ignorer l'e-mail de réinitialisation dans ce cas (l'e-mail doit le dire), ce qui signifie que votre système ne doit pas supprimer ou mettre à jour les mots de passe par lui-même.
Maxime Laval
3
@MaximeLaval C'est un très bon point. Vraiment, vous créez une demande de réinitialisation, ce qui serait un POST.
Craig Walker
12

Souvent, vous ne souhaitez pas supprimer ou détruire le mot de passe existant de l'utilisateur lors de la demande initiale, car cela peut avoir été déclenché (involontairement ou intentionnellement) par un utilisateur qui n'a pas accès à l'e-mail. Au lieu de cela, mettez à jour un jeton de réinitialisation du mot de passe sur l'enregistrement utilisateur et envoyez-le dans un lien inclus dans un e-mail. Cliquer sur le lien confirmera que l'utilisateur a reçu le jeton et souhaite mettre à jour son mot de passe. Idéalement, cela serait également sensible au facteur temps.

L'action RESTful dans ce cas serait un POST: déclenchement de l'action de création sur le contrôleur PasswordResets. L'action elle-même mettrait à jour le jeton et enverrait un e-mail.

Mark Swardstrom
la source
9

Je cherche en fait une réponse, pas l'intention d'en fournir une - mais "reset_password" me semble faux dans un contexte REST parce que c'est un verbe, pas un nom. Même si vous dites que vous faites un nom "action de réinitialisation" - en utilisant cette justification, tous les verbes sont des noms.

De plus, quelqu'un qui recherche la même réponse n'a peut-être pas pensé que vous pourriez obtenir le nom d'utilisateur via le contexte de sécurité et que vous ne deviez pas du tout l'envoyer via l'url ou le corps, ce qui me rend nerveux.

poisson-globe
la source
4
Cela reset-passwordressemble peut-être à un verbe, mais vous pouvez facilement l'inverser ( password-reset) pour en faire un nom. Et si vous avez modélisé votre application à l'aide de Event Sourcing ou même si vous avez juste n'importe quel type d'audit, il est logique que vous ayez en fait une véritable entité avec ce nom et que vous puissiez même autoriser les GET pour les utilisateurs ou les administrateurs à voir historique (masquant évidemment le texte du mot de passe). Cela ne me rend pas du tout nerveux. Et en ce qui concerne la récupération automatique du nom d'utilisateur côté serveur - vous pouvez, mais comment gérez-vous des choses comme l'administration / l'usurpation d'identité?
Aaronaught
1
Il n'y a rien de mal à utiliser verb dans REST. Tant qu'il est utilisé dans des endroits appropriés. Je pense que c'est plus un contrôleur qu'une resorce, et reset-passwordparviens à bien décrire ses effets.
Anders Östman
6

Je pense que la meilleure idée serait:

DELETE /api/v1/account/password    - To reset the current password (in case user forget the password)
POST   /api/v1/account/password    - To create new password (if user has reset the password)
PUT    /api/v1/account/{userId}/password    - To update the password (if user knows is old password and new password)

Concernant la fourniture des données:

  • Pour réinitialiser le mot de passe actuel

    • l'email doit être donné dans le corps.
  • Pour créer un nouveau mot de passe (après réinitialisation)

    • le nouveau mot de passe, le code d'activation et l'emailID doivent être indiqués dans le corps.
  • Pour mettre à jour le mot de passe (pour l'utilisateur connecté)

    • ancien mot de passe, le nouveau mot de passe doit être mentionné dans le corps.
    • UserId dans Params.
    • Jeton d'authentification dans les en-têtes.
codesnooker
la source
2
Comme indiqué dans d'autres réponses, "SUPPRIMER / api / v1 / compte / mot de passe" est une mauvaise idée, car n'importe qui peut réinitialiser le mot de passe de n'importe qui.
maximedupre
Nous avons besoin d'un identifiant de messagerie enregistré pour réinitialiser le mot de passe. Les chances de connaître l'identifiant de messagerie d'un utilisateur inconnu sont très sombres à moins que nous n'exécutions un site comme Facebook et que nous ayons des tonnes d'identifiants de courrier électronique collectés par tous les moyens. Ensuite, les politiques de sécurité seront définies en conséquence. Quelle est votre suggestion pour réinitialiser le mot de passe de quelqu'un?
codesnooker
5

Il y a quelques considérations à prendre:

Les réinitialisations de mot de passe ne sont pas idempotentes

Un changement de mot de passe affecte les données utilisées comme informations d'identification pour l'exécuter, ce qui pourrait par conséquent invalider les tentatives futures si la demande est simplement répétée textuellement alors que les informations d'identification stockées ont changé. Par exemple, si un jeton de réinitialisation temporaire est utilisé pour autoriser la modification, comme il est habituel dans une situation de mot de passe oublié, ce jeton doit expirer en cas de changement de mot de passe réussi, ce qui annule à nouveau les tentatives de réplication de la demande. Ainsi, une approche RESTful d'un changement de mot de passe semble être un travail mieux adapté POSTque PUT.

ID ou e-mail dans le chargement de données est probablement redondant

Bien que cela ne soit pas contraire à REST et puisse avoir un but particulier, il est souvent inutile de spécifier un identifiant ou une adresse e-mail pour une réinitialisation de mot de passe. Pensez-y, pourquoi fourniriez-vous l'adresse e-mail dans le cadre des données à une demande qui est censée passer par l'authentification d'une manière ou d'une autre? Si l'utilisateur change simplement son mot de passe, il doit s'authentifier pour ce faire (via nom d'utilisateur: mot de passe, email: mot de passe ou jeton d'accès fourni via les en-têtes). Par conséquent, nous avons accès à leur compte à partir de cette étape. S'ils avaient oublié leur mot de passe, ils auraient reçu un jeton de réinitialisation temporaire (par e-mail) qu'ils pourraient utiliser spécifiquement comme informations d'identification pour effectuer la modification. Et dans ce cas, l'authentification via jeton devrait suffire à identifier leur compte.

En tenant compte de tout ce qui précède, voici ce que je pense être le schéma approprié pour un changement de mot de passe RESTful:

Method: POST
url: /v1/account/password
Access Token (via headers): pwd_rst_token_b3xSw4hR8nKWE1d4iE2s7JawT8bCMsT1EvUQ94aI
data load: {"password": "This 1s My very New Passw0rd"}
Michael Ekoka
la source
Une déclaration selon laquelle un espace réservé nécessite des informations hors bande n'est pas entièrement vraie. Un type de support spécial peut décrire la syntaxe et la sémantique de certains éléments d'une requête ou d'une réponse. Il est ainsi possible pour un type de média de définir qu'un URI contenu dans un certain champ peut définir un espace réservé pour certaines données et la sémantique définit en outre qu'un utilisateur-email encodé ou non doit être inclus à la place de l'espace réservé. Les clients et serveurs respectant ce type de média seront toujours conformes aux principes d'architecture RESTful.
Roman Vottner
1
Concernant POSTvs. PUT RFC 7231 spécifie qu'une mise à jour partielle peut être réalisée par chevauchement des données de deux ressources, mais il est douteux que quelque chose comme /v1/account/passwordcompense réellement une bonne ressource. Tout POSTcomme le swiss-army-kniff du Web qui peut être utilisé si aucune des autres méthodes n'est faisable, il PATCHpourrait également être possible de définir le nouveau mot de passe.
Roman Vottner
qu'en est-il de l'URL pour demander la réinitialisation du mot de passe lorsqu'ils ne connaissent pas leur mot de passe?
dan carter
2

Je n'aurais pas quelque chose qui changerait le mot de passe et leur en enverrait un nouveau si vous décidez d'utiliser la méthode / users / {id} / password et que vous vous en tenez à votre idée que la demande est une ressource en soi. ie / user-password-request / est la ressource, et utilise PUT, les informations utilisateur doivent être dans le corps. Je ne changerais pas le mot de passe cependant, Id envoyer un e-mail à l'utilisateur qui contient un lien vers une page qui contient un request_guid, qui pourrait être transmis avec une demande à POST / user / {id} / password /? Request_guid = xxxxx

Cela changerait le mot de passe et ne permettrait pas à quelqu'un de détourner un utilisateur en demandant un changement de mot de passe.

De plus, le PUT initial pourrait échouer s'il y a une demande en suspens.

vélos
la source
0

Nous mettons à jour le mot de passe de l'utilisateur connecté PUT / v1 / users / password - identifiez l'identifiant de l'utilisateur à l'aide d'AccessToken.

L'échange d'identifiant d'utilisateur n'est pas sécurisé. L'API Restful doit identifier l'utilisateur à l'aide d'AccessToken reçu dans l'en-tête HTTP.

Exemple dans spring-boot

@putMapping(value="/v1/users/password")
public ResponseEntity<String> updatePassword(@RequestHeader(value="Authorization") String token){
/* find User Using token */
/* Update Password*?
/* Return 200 */
}
Dapper Dan
la source