2

Consider a web page where a user changes his password after clicking on an emailed password reset link.

The page will be implemented against an API using ajax, rather than as an ordinary form submission. That is, this question is about designing an API endpoint for changing a password.

Requirements

Any time a password is changed, the change must be recorded in the database in the password_changes table:

password_changes
  - user_id
  - date_changed

Specific Question

Thinking about this design from the perspective of API endpoints that create, update, or delete abstract "resources", does it make more sense to think about the password change as:

  1. We're updating the "user" resource, and hence should PUT a new password onto /user/{id} or perhaps /user/{id}/password? In this model, recording the change in password_changes is just an irrelevant implementation detail, and has no bearing on our concept of the "user" resource or the name of our endpoint.

  2. We're creating a "password_change". Thus we should be POST to /user/{id}/password_change, or something similar. In this model, the change to the "user" resource is just a side effect of creating a "password_change".

In this particular example, I think most people would choose option 1. And indeed it feels more natural to me too. Buy why? Is this just a matter of judgment, taste, or intuition? Or is there some codified principle of REST or even the basic concepts of resources and http actions that option 2. clearly violates?

(EDIT 3/2/2019: Fwiw, #2 now seems much more natural to me, and I'm confused why my former self felt #1 was more natural)

More General Question

What if we provide a history of password changes as a new enpoint: /user/{id}/password_changes?

That seems like a reasonable service.

But if we stick with design 1., now we make password changes by making PUTs to /user but view them with GETs to /user/{id}/password_changes. And then do we just disallow other http actions to password_changes (PUT, POST, DELETE)? That seems inconsistent... or maybe not?

Again, what are the rules (or even heuristics) governing these decisions? I can make judgment calls, but I'm looking for something more definitive.

1

In this particular example, I think most people would choose option 1. And indeed it feels more natural to me too. Buy why?

Not sure there's going to be a definitive answer to this, but my guess: because password, the entity, has a relatively straight forward representation as a document; HTTP's raison d'etre is document transfer, after all. It's pretty well designed for that.

Additionally, there's a simplicity to password - current state is a simple value type, and consequently there's no confusion about what "modify" should mean. Having only a single method available to mutate the state of the resource doesn't cause any problems because there's only one way you would want to: by completely replacing one representation with another, which is exactly the semantic of the PUT method in HTTP.

Generally speaking, a password change is not idempotent.

Yes, but that's a moot point here; the choice of method in this case is more driven by what should happen if a request is not acknowledged. Do we get to retry? If the message travels through an intermediate, is it allowed to retry the message rather than immediately returning a failure to the client?

(Admittedly, if we are throwing secrets around, we should be using an encrypted channel, which means that the intermediates can't see what's going on)

We aren't sending the request twice because we want the change applied twice, we are sending it twice to ensure that it is delivered at least once.

To ensure that the side effect on the domain doesn't get repeated, use a "conditional put", so that the server can distinguish between two copies of the same request, and two different requests that happen to contain the same representation of the password.

What if we provide a history of password changes as a new end point: /user/{id}/password_changes?

That seems like a reasonable service.

Yup. I would probably lean toward the spelling /user/{id}/password/history; it feels natural to make the audit log subordinate to the password resource.

And then do we just disallow other http actions to password_changes (PUT, POST, DELETE)? That seems inconsistent... or maybe not?

I don't see a consistency issue. Your domain model is the book of record for password changes; that history is not subject to override by the clients -- the domain reserves that privilege for itself. So the resource is read only. Ta-da?

all that said

We're creating a "password_change". Thus we should be POST to /user/{id}/password_change, or something similar. In this model, the change to the "user" resource is just a side effect of creating a "password_change".

This isn't wrong either; if you twist it around a little bit, you are basically describing AtomPub; you are publishing an entry (the PasswordChanged event) into a feed (the history), and the new "state" of the resource is derived from looking at the history. In other words, RESTful .

It's a bit crooked; the usual idiom is that commands are sent to an aggregate in the domain model, and the state changes that are caused by the command are published to a stream, which is exposed via an atom feed. In this use case, we turn it around, somewhat, by the fact that the human being, rather than the domain, is the book of record. Maybe.

Jim Webber describes REST application protocols as an analog to a 1950s style office, where you get things done by passing documents into inboxes. You deliver a ChangePasswordRequest to the server, and as a side effect the domain model updates itself, which has the cascading side effect of changing the state of other resources, like the audit log.

That's perhaps overkill for a simple ReplacePassword protocol, but is useful to keep in mind when things get to be more complicated.

  • You are quickly becoming my new favorite poster. I'm going to watch that video and may post a followup question. Thanks for the detailed answer! – Jonah Jul 2 '16 at 12:29
2

Semantically, I would consider a password change a POST. Why?

  1. You're not updating an entire resource (i.e. a User), but merely a property on that resource, a property that doesn't really have much to do with the resource's characteristics (like their address or hair color), except insofar as said property influences their ability to access the system. Since you can't retrieve a password with a USER object, it's not an intrinsic part of a USER for CRUD purposes, in other words.

  2. Generally speaking, a password change is not idempotent. Many systems won't let you re-use previous passwords, and a password change usually involves a complex dance of handing the user a magic url through their on-file email address and expiring it after a period of time. None of these actions are really consistent with the notion of a GET or PUT.

Verdict: POST.

What if we provide a history of password changes...

If your system is designed properly, neither you nor anyone else has this information. Well-designed systems store hashes of passwords, not the actual passwords, and you can't reverse-engineer the original passwords from the hashes.

  • Thanks for answer. I like your points about the semantics of passwords w.r.t users, and idempotency. If you look at the table password_changes in my OP, you'll see that your final point is a misunderstanding, as the table merely records the timestamp of the change and not the passwords themselves. It's a history of events. – Jonah Jul 2 '16 at 3:45
  • It's hard for me to see a legitimate use case for such information. – Robert Harvey Jul 2 '16 at 4:06
  • Nearly every ecommerce or banking site keeps records of password change events.... how is it hard for you to imagine why you would want to know when a user has changed a password? – Jonah Jul 2 '16 at 4:08
  • Ah, but you're not going to expose that endpoint to the outside world, right? You can get that information any way you like... Directly from the database, named pipe, whatever. You don't necessarily need a REST endpoint for that. – Robert Harvey Jul 2 '16 at 4:09
  • 1
    Direct database connection on the inside network. Save your mad REST skillz for the public facing connections. – Robert Harvey Jul 2 '16 at 4:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.