RESTful скидання пароля


107

Який правильний спосіб структурувати ресурс RESTful для скидання пароля?

Цей ресурс призначений для скидання пароля для тих, хто втратив або забув свій пароль. Це визнає недійсним їхній старий пароль та надсилає їм електронний лист.

У мене є два варіанти:

POST /reset_password/{user_name}

або ...

POST /reset_password
   -Username passed through request body

Я майже впевнений, що запит має бути POST. Я менш впевнений, що вибрав відповідне ім’я. І я не впевнений, чи слід ім’я користувача переходити через URL-адресу чи орган запиту.

Відповіді:


54

ОНОВЛЕННЯ: (далі коментар нижче)

Я б хотів щось подібне:

POST /users/:user_id/reset_password

У вас є колекція користувачів, де окремого користувача визначає {user_name}. Потім ви б вказали дію, над якою діяти, яка в даному випадку є reset_password. Це як би сказати "Створити ( POST) нову reset_passwordдію для {user_name}".


Попередня відповідь:

Я б хотів щось подібне:

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

У вас буде дві колекції, колекція користувачів та колекція атрибутів для кожного користувача. Користувача вказує :user_idатрибут, а атрибут визначає password. PUTОперація оновлює адресованих елементів колекції.


10
Я погоджуюся з вашим оновленим рішенням (POST). Запити PUT повинні бути ідентичними (тобто повторні запити не повинні впливати на результат). Це не стосується POST-запитів.
Росс Макфарлейн

16
Я б змінив reset_password на password_reset
Річард Ноп

9
Держіть, хлопці ... хіба це не дозволило б ВСЕГО скидати чийсь пароль? Так як, якщо це для тих, хто забув поточний пароль, зачепленого користувача не можна автентифікувати за допомогою поточного пароля. Таким чином, це означає, що цей API взагалі не може приймати жодного пароля - таким чином дозволяючи комусь скинути чийсь пароль, а якщо API поверне його, навіть отримати будь-який відомий пароль користувача ??? Або я щось пропускаю
transient_loop

39
Проблема з / користувачем / {id} / паролем тощо - це те, що ви можете не знати "id" користувача. Ви знаєте їх "ім'я користувача" чи "електронну пошту" чи "телефон", але не "ідентифікатор".
coolaj86

17
Основний недолік такого підходу полягає в тому, що він передбачає, що ви вже знаєте ідентифікатор користувача. Це буде правдою в деяких обставинах, але як це робити тоді, коли ім’я користувача або ідентифікатор користувача невідомі, якщо користувачеві потрібно лише вказати електронний лист для скидання.
Алаппін

94

Неавторизовані користувачі

Ми робимо PUTзапит у api/v1/account/passwordкінцевій точці та вимагаємо параметр із відповідною електронною поштою облікового запису для ідентифікації облікового запису, для якого користувач хоче скинути (оновити) пароль:

PUT : /api/v1/account/password?email={email@example.com}

Примітка. Як згадував @DougDomeny у своєму коментарі, передача електронної пошти як рядка запиту в URL- адресі є ризиком для безпеки. Параметри GET не піддаються впливу під час використання https(і ви завжди повинні використовувати належне httpsз'єднання для таких запитів), але є інші ризики безпеки. Більше про цю тему ви можете прочитати в цій публікації в блозі тут .

Передача електронної пошти в орган запиту була б більш безпечною альтернативою передачі її як параметр GET:

PUT : /api/v1/account/password

Орган запиту:

{
    "email": "email@example.com"
}

Відповідь має 202прийняте значення відповіді:

Запит прийнято для обробки, але обробка не завершена. Зрештою на цей запит не може бути застосовано, оскільки це може бути заборонено, коли обробка фактично відбувається. Немає можливості повторного надсилання коду стану з асинхронної операції, такої як ця.

Користувач отримає електронний лист на адресу email@example.comта обробка запиту на оновлення залежить від дій, здійснених із посиланням з електронного листа.

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

Відкриття посилання з цього електронного листа направить на форму скидання пароля на додатковому додатку, який використовує маркер пароля скидання з посилання як вхід для прихованого поля введення (маркер є частиною посилання як рядок запиту). Ще одне поле введення дозволяє користувачеві встановити новий пароль. Другий вхід для підтвердження нового пароля буде використаний для перевірки на передній панелі (щоб запобігти помилкам друку).

Примітка: В електронному листі ми також можемо зазначити, що у випадку, якщо користувач не ініціалізує скидання пароля, він / вона може ігнорувати електронну пошту та продовжувати користуватися програмою, як правило, своїм поточним паролем

Коли форма подається з новим паролем та маркер як вхід, відбудеться процес скидання пароля. Дані форми будуть надсилатись із PUTзапитом ще раз, але на цей раз, включаючи маркер, і ми замінимо пароль ресурсу на нове значення:

PUT : /api/v1/account/password

Орган запиту:

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

Відповідь буде 204не зміст відповіді

Сервер виконав цей запит, але йому не потрібно повертати тіло об'єкта і, можливо, захоче повернути оновлену інформацію. Відповідь МОЖЕ включати нову або оновлену метейнформацію у вигляді заголовків сутностей, які, якщо вони є, ОБОВ'ЯЗКОВО бути пов'язані із запитуваним варіантом.

Автентифіковані користувачі

Для аутентифікованих користувачів, які хочуть змінити свій пароль, PUTзапит можна виконати негайно без електронної пошти (обліковий запис, на який ми оновлюємо пароль, відомий серверу). У такому випадку у форму надсилаються два поля:

PUT : /api/v1/account/password

Орган запиту:

{
    "old":"password",
    "new":"password"
}

У першому абзаці ви кажете PUT, але в наведеному нижче прикладі йде DELETE. Що точно?
jpierson

Це не відкриває адресу електронної пошти за URL-адресою, що було б більш безпечним для даних JSON.
Дуг Домені

@DougDomeny Так, надіслати повідомлення електронною поштою як json, можливо, буде краще. Я додав це у відповідь як альтернативний більш безпечний варіант, інакше рішення може бути таким же.
Заповіт

@Wilt: Хіба це не буде операцією PATCH? PUT вимагає відправити повний ресурс
j10

1
@jitenshah Добре. Коли я писав це, я думав, що PUT буде кращим, але я не пам'ятаю, чому саме. Я погоджуюся з вашими міркуваннями, що патч може бути більш підходящим для даного випадку.
Загинув

18

Давайте отримаємо uber-RESTful на секунду. Чому б не застосувати дію DELETE для пароля, щоб викликати скидання? Має сенс, чи не так? Зрештою, ви ефективно відкидаєте існуючий пароль на користь іншого.

Це означає, що ви зробите:

DELETE /users/{user_name}/password

Тепер два великих застереження:

  1. HTTP DELETE повинен бути ідентичним (фантастичне слово для того, щоб сказати "нічого не робити, якщо ви зробите це кілька разів"). Якщо ви робите стандартні речі, такі як надсилання електронного листа "Скидання пароля", то у вас виникнуть проблеми. Ви можете обійти це позначення користувача / пароля булевим прапором "Скидання". При кожному видаленні ви перевіряєте цей прапор; якщо він не встановлений , то ви можете скинути пароль і відправити по електронній пошті. (Зверніть увагу, що наявність цього прапора може мати й інші потреби.)

  2. Ви не можете використовувати HTTP DELETE через форму , тому вам доведеться здійснити дзвінок AJAX та / або тунель ВИДАЛИТИ через пошту.


9
Цікава ідея. Однак я не вважаю, DELETEщо тут добре вписується. Можливо, ви будете замінювати пароль випадковим чином, таким чином, ви DELETEможете ввести в оману. Я вважаю за краще те Create (POST) new reset_password action, де іменник (ресурс), на який ви діяли б, - це "reset_password action". Це добре підходить і для надсилання електронних листів, оскільки дія включає в себе всі ці дані нижчого рівня. POSTне є безсильним.
Даніель Вассалло

Мені подобається пропозиція. Випуск 1 може бути вирішений за допомогою умовних запитів, тобто HEAD, який надсилає заголовки ETag + DELETE та If-Match. Якщо хтось спробує видалити неактивний пароль, він отримає номер 412.
whiskeysierra

1
Я б уникнув DELETE. Ви оновлюєте, оскільки та сама сутність / концепція отримає нове значення. Але насправді це зазвичай навіть не відбувається зараз, а лише після відправлення нового пароля в пізніше іншому запиті (після скидання пароля пошти) - нині ніхто не надсилає новий пароль поштою, а маркер, щоб скинути його в новому запиті з заданий жетон, правда?
antonio.fornie

11
Що робити, якщо користувач запам'ятає свій пароль відразу після подання запиту на скидання? А як щодо бота, який намагається скинути випадкові рахунки? У такому випадку користувачеві слід дозволити ігнорувати електронну пошту для скидання (електронна пошта повинна сказати так), тобто система не повинна видаляти та не оновлювати паролі самостійно.
Maxime Laval

3
@MaximeLaval Це дуже вдалий момент. Дійсно, ви "створюєте запит на скидання", який би був POST.
Крейг Уокер

12

Часто ви не хочете видаляти або знищувати існуючий пароль користувача при первинному запиті, оскільки це, можливо, було спровоковано (ненавмисно або навмисно) користувачем, який не має доступу до електронної пошти. Натомість оновіть маркер пароля для запису користувача та надішліть його у посиланні, включеному в електронний лист. Натиснувши на посилання, підтвердите, що користувач отримав маркер і побажав оновити свій пароль. В ідеалі це також буде залежно від часу.

Дією RESTful у цьому випадку буде POST: запуск дії створення на контролері PasswordResets. Сама дія дозволила б оновити маркер і надіслати електронний лист.


9

Я насправді шукаю відповідь, не маючи сенсу надати її, але "reset_password" звучить для мене неправильно в контексті REST, оскільки це дієслово, а не іменник. Навіть якщо ви говорите, що робите іменник "скидання дії" - використовуючи це виправдання, всі дієслова є іменниками.

Крім того, хтось, хто шукає ту саму відповідь, може не прийти в голову, що ви можете отримати ім'я користувача через контекст безпеки, і не доведеться надсилати його через URL або тіло взагалі, що мене нервує.


4
Можливо, це reset-passwordзвучить як дієслово, але ви можете легко password-resetперетворити його ( ), щоб зробити його іменником. І якщо ви моделювали свою програму за допомогою подій Sourcing або навіть якщо ви просто проводили будь-який аудит, має сенс, що ви насправді маєте реальну сутність з цим ім'ям і навіть можете дозволити GETs в ньому користувачам або адміністраторам бачити історія (очевидно, маскування тексту пароля). Це мене зовсім не нервує. А що стосується автоматичного підбору імені користувача на стороні сервера - ви можете, але як потім ви керуєтесь такими речами, як адміністрування / видавання себе за себе?
Aaronaught

1
У використанні дієслова в REST немає нічого поганого. До тих пір, поки він використовується у відповідних місцях. Я думаю, що це скоріше контролер, ніж курорт, і reset-passwordвдається добре описати його ефекти.
Андерс Естман

6

Я думаю, що кращою ідеєю було б:

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)

Щодо надання даних:

  • Щоб скинути поточний пароль

    • електронну пошту потрібно вказати на тілі.
  • Щоб створити новий пароль (після скидання)

    • новий пароль, код активації та emailID повинні бути вказані в корпусі.
  • Оновити пароль (для користувача, що ввійшли в систему)

    • старий пароль, новий пароль слід згадати в корпусі.
    • UserId в Парамах.
    • Auth Token у заголовках.

2
Як коментується в інших відповідях, "DELETE / api / v1 / account / password" - це погана ідея, оскільки будь-хто може скинути будь-який пароль.
maximedupre

Нам потрібен зареєстрований ідентифікатор електронної пошти, щоб скинути пароль. Шанси дізнатись ідентифікатор електронної пошти невідомого користувача дуже похмурі, якщо ми не запускаємо сайт, як Facebook, і не збираємо безліч ідентифікаторів електронних листів будь-якими способами. Тоді політика безпеки буде визначена відповідно. Яка пропозиція змінити пароль когось?
codenooker

5

Потрібно взяти до уваги кілька міркувань:

Скидання пароля не є ідентичним

Зміна пароля впливає на дані, які використовуються як облікові дані для його виконання, що може призвести до недійсності майбутніх спроб, якщо запит буде повторно дослівно повторений, тоді як збережені облікові дані будуть змінені. Наприклад, якщо тимчасовий маркер скидання використовується для дозволу зміни, як це прийнято в ситуації забутого пароля, цей маркер повинен бути закінчений після успішної зміни пароля, що знову зводить нанівець подальші спроби реплікації запиту. Таким чином, здається, що RESTful підхід до зміни пароля є роботою, яка краще підходить для, POSTніжPUT .

Ідентифікатор або електронна пошта при завантаженні даних, ймовірно, зайві

Хоча це не проти REST і може мати якусь особливу мету, часто не потрібно вказувати ідентифікатор або електронну адресу для скидання пароля. Подумайте над тим, чому б ви надали адресу електронної пошти як частину даних запиту, який повинен пройти автентифікацію так чи інакше? Якщо користувач просто змінює свій пароль, для цього потрібно пройти автентифікацію (через ім’я користувача: пароль, електронну пошту: пароль або маркер доступу, наданий через заголовки). Отже, ми маємо доступ до їх облікового запису з цього кроку. Якби вони забули свій пароль, їм було б надано тимчасовий маркер скидання (електронною поштою), який вони можуть використовувати конкретно як облікові дані для внесення змін. І в цьому випадку автентифікації через маркер має бути достатньо для ідентифікації їх облікового запису.

Беручи до уваги все вищесказане, ось що я вважаю правильною схемою зміни RESTful пароля:

Method: POST
url: /v1/account/password
Access Token (via headers): pwd_rst_token_b3xSw4hR8nKWE1d4iE2s7JawT8bCMsT1EvUQ94aI
data load: {"password": "This 1s My very New Passw0rd"}

Заява про те, що для заповнювача потрібна інформація про позадіапазонну інформацію, не є абсолютно правдивою. Спеціальний тип носія може описувати синтаксис та семантику певних елементів запиту чи відповіді. Таким чином, для типу медіа можна визначити, що URI, що міститься в певному полі, може визначати заповнювач місця для певних даних, а семантика додатково визначає, що кодований електронний лист користувача або те, що не повинно бути включено замість заповнення місця. Клієнти та сервери, які поважають цей тип медіа, все ще будуть відповідати принципам архітектури RESTful.
Роман Воттнер

1
Що стосується по POSTпорівнянні з PUT RFC 7231 визначає , що часткове оновлення може бути досягнуто за рахунок перекриваються даних двох ресурсів , але сумнівно , якщо що - щось подібне /v1/account/passwordробить дійсно заповнюють хороший ресурс на насправді. Як POSTі швейцарський армійський нож в Інтернеті, який можна використовувати, якщо жоден з інших методів неможливий, врахування PATCHтакож може бути вибором для встановлення нового пароля.
Роман Воттнер

як щодо URL-адреси, щоб запросити скидання пароля, коли вони не знають свого пароля?
дан картер

2

У мене не було б чогось змінити пароль і надіслати їм новий, якщо ви вирішите використовувати метод / users / {id} / password, і дотримуйтесь вашої думки, що запит - це власний ресурс. тобто / користувач-пароль-запит / - це ресурс і використовується PUT, інформація про користувача повинна бути в тілі. Я б не змінив пароль, я надсилаю електронному листу користувачеві повідомлення, яке містить посилання на сторінку, яка містить request_guid, яку можна передати разом із запитом на POST / user / {id} / password /? Request_guid = ххххх

Це дозволить змінити пароль, і це не дозволяє комусь дозволити користувачеві за допомогою шлангу, вимагаючи зміни пароля.

Плюс початковий PUT може не вдатися, якщо є непогашений запит.


0

Ми оновлюємо пароль користувача PUT / v1 / users / password - ідентифікуємо ідентифікатор користувача за допомогою AccessToken.

Обміняти ідентифікатор користувача не безпечно. API Restful повинен ідентифікувати користувача, використовуючи AccessToken, отриманий у заголовку HTTP.

Приклад у весняному завантаженні

@putMapping(value="/v1/users/password")
public ResponseEntity<String> updatePassword(@RequestHeader(value="Authorization") String token){
/* find User Using token */
/* Update Password*?
/* Return 200 */
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.