Quelle est la manière appropriée de structurer une ressource RESTful pour réinitialiser un mot de passe?
Cette ressource est censée être un mot de passe pour une personne qui a perdu ou oublié son mot de passe. Cela invalide leur ancien mot de passe et leur envoie un mot de passe par courrier électronique.
Les deux options que j'ai sont:
POST /reset_password/{user_name}
ou...
POST /reset_password
-Username passed through request body
Je suis à peu près sûr que la demande devrait être un POST. Je suis moins sûr d'avoir choisi un nom approprié. Et je ne suis pas sûr si le nom d'utilisateur doit être transmis via l'URL ou le corps de la demande.
UPDATE: (suite au commentaire ci-dessous)
Je voudrais 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 pouvez alors spécifier l'action à exécuter, qui dans ce cas est reset_password
. C'est comme dire "Créez (POST
) une nouvelle action reset_password
pour {user_name}
".
Réponse précédente:
Je voudrais 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 :user_id
et l'attribut par password
. L'opération PUT
met à jour le membre adressé de la collection.
Nous faisons une demande PUT
sur un noeud final api/v1/account/password
et demandons un paramètre avec le courrier électronique du 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]}
Note: _ Comme @DougDomeny mentionné dans son commentaire, le courrier électronique, une chaîne de requête dans l'URL est un risque pour la sécurité. Les paramètres GET ne sont pas exposés lors de l'utilisation de https
(et vous devriez toujours utiliser connexion https
appropriée pour de telles demandes), mais il existe d'autres risques de sécurité.Vous pouvez en savoir plus sur ce sujet dans ce billet de blog ici .
Transmettre le courrier électronique dans le corps de la requête constituerait une alternative plus sûre que le transmettre en tant que paramètre GET:
PUT : /api/v1/account/password
Corps de la demande:
{
"email": "[email protected]"
}
La réponse a une réponse 202
acceptée qui signifie:
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’existe aucune possibilité de renvoyer un code d’état à partir d’une opération asynchrone comme celle-ci.
L'utilisateur recevra un email à l'adresse [email protected]
et le traitement de la demande de mise à jour dépend des actions entreprises avec le lien de l'email.
https://example.com/password-reset?token=1234567890
Si vous ouvrez le lien à partir de cet e-mail, un formulaire de réinitialisation du mot de passe de l'application frontale 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 serveur frontal (pour éviter les fautes de frappe).
Remarque: _ Dans le courrier électronique, nous pourrions également mentionner que si l'utilisateur n'initialise aucune réinitialisation de mot de passe, il peut ignorer le courrier électronique 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 en tant qu'entrées, le processus de réinitialisation du mot de passe a lieu. Les données du formulaire seront à nouveau envoyées avec une demande PUT
, mais cette fois-ci avec le jeton, nous remplacerons le mot de passe de la ressource par une nouvelle valeur:
PUT : /api/v1/account/password
Corps de la demande:
{
"token":"1234567890",
"new":"password"
}
La réponse sera une réponse 204
no content
Le serveur a répondu à la demande mais n'a pas besoin de renvoyer un corps d'entité et peut vouloir renvoyer des métainformations mises à jour. La réponse PEUT inclure des métainformations nouvelles ou mises à jour sous la forme d'en-têtes d'entité, qui, le cas échéant, DEVRAIENT être associées à la variante demandée.
Pour les utilisateurs authentifiés qui souhaitent modifier leur mot de passe, la demande PUT
peut être effectuée immédiatement sans le courrier électronique (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
Corps de la demande:
{
"old":"password",
"new":"password"
}
Soyons uber-RESTful pendant une seconde. Pourquoi ne pas utiliser l'action DELETE pour le mot de passe pour déclencher une réinitialisation? C'est logique, n'est-ce pas? Après tout, vous supprimez 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:
HTTP DELETE est supposé être idempotent (un mot sophistiqué pour dire "pas grave si vous le faites plusieurs fois"). Si vous utilisez des méthodes standard, telles que l'envoi d'un courrier électronique "Réinitialisation du mot de passe", vous rencontrerez des problèmes. Vous pouvez contourner ce problème en balisant l'utilisateur/mot de passe avec un indicateur booléen "Is Reset". À chaque suppression, vous cochez cette case; s'il n'est pas défini alors, vous pouvez réinitialiser le mot de passe et envoyer votre courrier électronique. (Notez que le fait d’avoir ce drapeau pourrait aussi avoir d’autres utilisations.)
Vous ne pouvez pas utiliser HTTP DELETE via un formulaire , vous devrez donc passer un appel AJAX et/ou tunneler DELETE via le POST.
Souvent, vous ne souhaitez pas supprimer ou détruire le mot de passe existant de l'utilisateur lors de la demande initiale, ce dernier pouvant avoir été déclenché (involontairement ou intentionnellement) par un utilisateur n'ayant pas accès au courrier électronique. 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 courrier électronique. En cliquant sur le lien, vous confirmez que l'utilisateur a reçu le jeton et souhaite mettre à jour son mot de passe. Idéalement, cela serait également sensible au temps.
L'action RESTful dans ce cas serait un POST: déclenchant l'action create sur le contrôleur PasswordResets. L'action elle-même mettrait à jour le jeton et enverrait un courrier électronique.
En fait, je cherche une réponse qui ne signifie pas en fournir une, mais "reset_password" ne me semble pas correct dans un contexte REST car il s'agit d'un verbe et non d'un nom. Même si vous dites que vous faites un nom "d'action de réinitialisation" - en utilisant cette justification, tous les verbes sont des noms.
En outre, une personne cherchant la même réponse n'a peut-être pas pensé que vous pourriez obtenir le nom d'utilisateur à travers le contexte de sécurité et ne pas avoir à l'envoyer via l'URL ou le corps du tout, ce qui me rend nerveux.
Je pense qu'une 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)
En ce qui concerne la fourniture des données:
Pour réinitialiser le mot de passe actuel
Pour créer un nouveau mot de passe (après réinitialisation)
Pour mettre à jour le mot de passe (pour l'utilisateur connecté)
Il y a quelques considérations à prendre en compte:
Un changement de mot de passe affecte les données utilisées comme identifiants pour le réaliser, ce qui pourrait invalider les tentatives futures si la demande est simplement répétée intégralement alors que les identifiants stockés ont changé. Par exemple, si un jeton de réinitialisation temporaire est utilisé pour autoriser la modification, comme il est d'usage en cas de mot de passe oublié, ce jeton doit expirer lors de la modification du mot de passe, ce qui annule à nouveau les tentatives de réplication de la demande. Ainsi, une approche RESTful pour changer le mot de passe semble être un travail mieux adapté à POST
qu'à PUT
.
Une ressource spécifiée en tant que /password-reset/{user_id}
implique que l'url réelle pointant sur la ressource doit être générée avec des informations hors bande (c'est-à-dire que nous devons avoir une connaissance préalable de user_id
), et non acquises par découverte. Cela suppose en outre que nous aurons accès à ces informations au moment de la demande, ce qui ne sera souvent pas le cas.
Bien que cela ne soit pas contre REST et puisse avoir un objectif particulier, il est souvent inutile de spécifier un ID ou une adresse électronique pour une réinitialisation de mot de passe. Pensez-y, pourquoi voudriez-vous fournir l'adresse électronique dans les données d'une demande 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 le faire (via nom d'utilisateur: mot de passe, email, mot de passe ou jeton d'accès fourni via des 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 courrier électronique) qu'ils peuvent utiliser spécifiquement comme identifiants pour effectuer le changement. Et dans ce cas, l’authentification par jeton devrait suffire à identifier son compte.
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:
L'utilisateur connaît son mot de passe:
Method: POST
url: /v1/account/password
Auth (via headers): [email protected]:my 0ld pa55Word
data load: {"password": "This 1s My very New Passw0rd"}
L'utilisateur ne connaît pas son mot de passe (réinitialisation du mot de passe par courrier électronique):
Method: POST
url: /v1/account/password
Access Token (via headers): pwd_rst_token_b3xSw4hR8nKWE1d4iE2s7JawT8bCMsT1EvUQ94aI
data load: {"password": "This 1s My very New Passw0rd"}
Je ne voudrais pas que quelque chose change le mot de passe et en envoie un nouveau si vous décidez d'utiliser la méthode/users/{id}/password et si vous vous en tenez à l'idée que la demande est une ressource en soi. C'est-à-dire/user-password-request/est la ressource, et utilise PUT, les informations sur l'utilisateur doivent figurer dans le corps de l'écran . Je ne changerais pas le mot de passe par contre, Id envoie un courrier électronique à l'utilisateur contenant un lien vers un page qui contient un request_guid, qui pourrait être transmis avec une requête à POST/user/{id}/password /? request_guid = xxxxx
Cela changerait le mot de passe, et cela ne permettrait pas à quelqu'un d'arroser un utilisateur en demandant un changement de mot de passe.
De plus, le PUT initial pourrait échouer s'il y a une demande en attente.
Nous mettons à jour le mot de passe de l'utilisateur connecté PUT/v1/users/password - identifie l'identifiant de l'utilisateur à l'aide d'AccessToken.
Il n'est pas sécurisé d'échanger un identifiant d'utilisateur. L'API restante doit identifier l'utilisateur à l'aide d'AccessToken reçu dans l'en-tête HTTP.
Exemple en botte de printemps
@putMapping(value="/v1/users/password")
public ResponseEntity<String> updatePassword(@RequestHeader(value="Authorization") String token){
/* find User Using token */
/* Update Password*?
/* Return 200 */
}