API Design – Best Practices for Changing a User’s Password

apiapi-designhttprest

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.

Best Answer

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.

Related Topic