Skip to main content

The pedantic guide for a RESTful Registration Use Case

Posted by felipegaucho on October 2, 2009 at 3:15 AM PDT

Registration is one of most implemented use cases ever, but things get a bit different when you try to implement it in a RESTful Web-Service.

Before I start the discussion about the registration use case, a
list of terms for disambiguation:

  • Application means a href="http://java.sun.com/javaee/">Java EE Application. ( href="http://kenai.com/projects/puj/pages/Home">Arena-PUJ

    is the Java EE Application I am doing my REST experiments)

  • Service is a RESTful Web-Service. (back-end
    of a Java EE Application)
  • Client Application is any software that can
    communicate with the Service using the HTTP Protocol. (front-end

    of a Java EE Application)

  • End-user is a person interacting with an
    Application.

The Registration Use Case

The registration use case I am taking as example is the one I
just implemented in the href="http://kenai.com/projects/puj/pages/Home">PUJ Project - a
classical Registration User History adapted to the REST
terminology:

  1. An end-user opens a client application and type some personal
    information like name, email and password in a form.
  2. When the customer presses a [submit button], the client
    application do a POST request to a RESTful web-service.
  3. The web-service handles the registration in three steps:
    1. Validates the customer information (form data)
    2. Creates a new account record in the database
    3. Sends a confirmation email to the new
      customer.
  4. The web-service responds OK (HTTP 200) to the client
    application. The client application informs the customer that he will
    receive a confirmation email and how to proceed from that.
  5. The customer receives an email containing a confirmation URL
    he needs to click to activate his account.
  6. The customer clicks on the confirmation URL to activate his
    new account.
  7. Once the web-service receives the HTTP GET

    request from the email URL, it changes the status of
    the account
    resource from NEW to ACTIVE and
    redirect the customer to a further instructions URI (HTTP
    303).

The above use case has several variations, and I omitted the
exceptions for the sake of clearness. The important points for our
discussion are highlighted in bold font and explained below.

Confirmation e-mails are not REST

A Natural Born RESTafarian
would realize by a glance the issue here, but if you are not that
updated with the HTTP
Protocol
and the href="http://www.slideshare.net/trilancer/why-hateoas-1547275">HATEOAS
constraints, here comes the problem:

An HTTP GET method is href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.2">Idempotent,
what means it should never change the state of a
resource (URI)
either by direct invocation or its side effects. As soon
the customer receives the email and clicks on the confirmation URL, he
is triggering a GET that will change the status of his
account from 'NEW' to 'ACTIVE'. This status change should never be
done through a GET method if you want your Service API to continue to be
called REST.

You can take a rest now, a coffee, take a while
to digest the idea that most of the confirmation emails out there are
not REST
. Doesn't matter how beautiful your Web-Service is, if an end
user clicks on a GET URL to confirm his registration, your efforts were
in vain - your API is not REST.

href="http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven">Flames
aside, the simple fact is that e-mails containing a confirmation URL is
the de
facto standard
solution for the registration use case on the web. Due to
that reason I adopted the word pedantic in the title of this
blog, because most of the developers just ignore that detail and go on
with the widely adopted solution. I will keep the pedantic mood for now,
and the rest of this post is to explain you the workaround I adopted to
keep the chances to call my service as RESTful API. I will start
enumerating the options you have once you realize the conflict between
the REST constraints and the GET URL in a confirmation e-mail:

  1. Ignore the problem and just use the GET URL in
    the confirmation email. That was one of the most suggested solutions in
    the several mailing lists I discussed the conceptual problem. A lazy
    but yet very pragmatic solution since it works like a charm. If you can
    sleep at night knowing your API is abusing the HTTP Protocol, go for it
    (and then you can stop reading this blog). IMO: it is just irritating
    to know I am coding the wrong thing, and since I am producing a research-purpose-open-source-pet-project
    I will try something more elegant.

  2. Delegate the security responsibility to the
    client
    , leaving the Service API 100% REST compliant. You can secure the
    communication layer between the client and the server and then you
    don't need the security information on the service layer anymore. The client
    becomes the security master
    and every request done by the client is
    blindly attended by the server. It sounds a good alternative, but the
    problem is: sensitive resources should be protected in your service
    layer. To delegate the data encapsulation to an external layer seems
    just too risky - unless you produce the service and the client by
    yourself and you have some way of guaranteeing the information
    security. IMO: sooner or later this decision will cost some stress in
    terms of maintenance.

  3. Use the SMTP to handle the registration, asking
    the user to respond the email instead of clicking in the URL. IMO: it
    solves a problem creating many others because as soon you have an Inbox
    in your solution you also have SPAMs and your customers trying to send
    e-mails to ask something in that Inbox, etc. Also, the confirmation is
    asynchronous and dependent of external infra-structure. There is no
    guarantee of delivering of the customer e-mail - the SMTP server of the
    customer has a problem, and it becomes your application problem too.
    There is a delay between the moment your customer respond the email and
    the moment his account is actually activated. IMO: More configuration
    requirements, more maintenance issues, a potential mess. But no, I
    don't have a killer argument against that, just a strong feeling
    against that.

  4. Use a mediator between the service and the
    end user
    , forcing the registration operations to pass through a
    client-application first. When the user clicks on the URL in the
    confirmation email, he will access a client application (and not
    the service), and the client application is responsible to handle the
    request - eventually doing a POST to confirm the registration, what is
    the REST way of doing that. That little change in the registration
    workflow makes the API RESTful again and also add some extra security
    and usability features. It causes some impact in the user experience,
    but I believe the complexity added to the user is minimum and quite
    acceptable. IMO: that is the best solution, and after implementing that
    I also found other benefits of that decision, as I explain below.

Some interactions between an End-User and a RESTful Web-Service
should be intermediated by a Client Application

The problem of our previous Use Case is a GET method changing the
state of an account resource (URI) and it contradicts the
idempotence property of the HTTP GET method. Due to that I rewrote the
original use case to fits the REST constraints.

A RESTful version of the Registration Use Case

  1. An end-user opens a client application and type some personal
    information like name, email and password in a form.
  2. When he customer presses a [submit button], the client
    application do a POST request to a RESTful web-service, including
    the URL where the user should visit in order to confirm his
    registration
    .
  3. The web-service handles the registration in three steps:
    1. Validates the customer information (form data)
    2. Creates a new account record in the database
    3. Sends a confirmation email to the new
      customer.
  4. The web-service responds OK (HTTP 200) to the client
    application. The client application informs the customer that he will
    receive a confirmation email and how to proceed from that.
  5. The customer receives an email containing a confirmation URL
    he needs to click to activate his account.
  6. The customer clicks on the confirmation URL to activate his
    new account - and get redirected to the client
    application (not directly to the service).
    .
  7. The Client Application receives the HTTP GET Request and
    proceed the confirmation of the registration doing a HTTP POST to the
    Web-Service
  8. Once the web-service receives the HTTP POST
    request from the Client Application, it changes the
    status of the account
    resource from NEW to ACTIVE

    and responds OK (HTTP 200) to the client application.

  9. The Client Application notifies the end user that his account
    was activated.

Notice that only two things actually changed from the original use
case:

  • The data sent from the client application in the step 2 contains
    now an URL to be used to compose the confirmation URL included in the
    confirmation email. The service no more build this URL based on its own
    domain. The hash included in the message remains the same, and the logic
    to validate the registration in the server also remains the same.

  • The navigation of the user in the step 6 changed from "a
    Service Resource
    " to "a Client Application resource". The
    Client application should handle that, asking the end user to click a
    button or even using some automatic feature to redirect the end user
    GET Request to the Web-Service POST Request. The Client Application is
    the mediator between the end user and the web-service.

Benefits of the mediator in the Registration Use Case

  • The confirmation is done through a GET Idempotent method, so
    the user can clicks several times in the email URL without unwanted
    side-effects. Who handles the confirmation operation is the Client
    Application and the back-end part of the Java EE application remains a
    pure REST API.
  • The end user doesn't need to know where the web service is
    hosted, right? Less exposed the RESTful Service is, more secure it is.
  • Several applications can share the same service, using
    different languages and technologies to interact with its end users.
    Your service don't need to know about how these applications
    communicate with their users.
  • The Client Application can handle the end users requests in a
    much more friendly way, including the localization of the messages and
    pages shown to the end-user. This would be a difficult thing to do in
    the server side, specially if the same server supports several Client
    Applications.
  • If the service has a temporary problem, the Client Application
    can give the user some explanation about that, much better than an HTTP
    Error code.
  • If the service change its host address, the Client Application
    can adapt its addresses mapping without bothering the end users. If a
    user receives a confirmation email and the service changed the host
    address, the user will never have any problem because that.

The idea of a mediator may be adopted in several other scenarios,
just thing a few minutes about that.

Try it online

You can try the RESTful Registration Use Case on my online demo,
just do that:

  1. curl -d
    "name=NAME&password=PASSWORD&login=LOGIN&email=EMAIL&url=http://fgaucho.dyndns.org:8080/arena-dwr/confirm"
    http://fgaucho.dyndns.org:8080/arena-http/user

    Where:

    • NAME: your name (max 20 characters)
    • PASSWORD: a password (max 32 characters)
    • LOGIN: your user name (max 20 characters)
    • EMAIL: your email (you should use a real email
      in order to receive the confirmation message.
      )

    An example:

    curl -d
    "name=Felipe&password=test&login=fgaucho&email=useless@gmail.not&url=http://fgaucho.dyndns.org:8080/arena-dwr/confirm"
    http://fgaucho.dyndns.org:8080/arena-http/user

Notice you are sending a POST Request to the address http://fgaucho.dyndns.org:8080/arena-http/user.
After receiving the confirmation email, click on it to see that you are
going to other address. You can try that without risk since it is my
personal machine (lab) and all data will be erased in the next commit.
Other point: the activation of accounts are not enabled, so the whole
registration process should work, but you will not be able to do much
with your new account anyway :). If you are just paranoid about your
private data, use a temporary email for testing or do create a fake
email account somewhere.

Are you using an Operational System without the CURL tool?
Perhaps this
link
can can help you.

Summary

REST is a good architecture style, sometimes it imposes
limitations on what you can do, specially if you consider that new
protocols are coming to the game soon: SMS, SMTP, Digital TV ones, etc.
The HTTP protocol should be preserved to keep the web clean and robust
as we know it. if you abuse the HTTP Protocol due to commercial
benefits, it is important to make it clear that you are doing that for a
reason and more important: to accept that if you abuse any protocol you
are not using the protocol anymore. It may sounds too pedantic, or not,
and I leave a large space here for your considerations and eventually
for proposing a better solution. We are all here to learn, in this times
of flames it is good to spread this motto ;)

My self-education on the REST technologies are still in the
beginning and I am thankful for all of those sharing their knowledge
about that.

Comments

That's not exactly what idempotent means

Idempotent means "if you do it twice, it's the same as doing it once". (http://mathworld.wolfram.com/Idempotent.html) http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.2 explicitly mentions the possibility of side effects, so "idempotent" is not the same as "no side effects".

So, technically, wouldn't a confirmation link be idempotent? If a user confirms twice, it's the same as confirming once. (Unless you want the second response to be "Hey you dummy...you already confirmed!"...) But let's say the response is simply "Thank you for confirming..." What is not RESTful about such a confirmation link?

GET cannot change the state of a resource

The problem is: before the GET an "user" resource has a set of attributes in one state, and after the GET the same record changed in the database. The information associated to the resource is not the same and the different is a direct effect of the first GET call. It is not about the response message, it is about what happens when the server side processes the GET Request - a database change is not idempotent.

Step 3c

I'm working on two new REST projects so I'm following your blog closely. Thanks for the frequent updates!

In step 3c of your final solution, the web service sends the email. I'm having a hard time deciding if I like that or not, especially if the registration service gets used by multiple clients, possibly for multiple different applications. It would be difficult for the email to be customized properly (different "Thank you" message for each client).

Is there a way for the client to send the email and not fall into the trap of what you wrote in solution possibility #2, Delegate the security responsibility to the client?

Is there any value that could be returned by the original POST to the web service that the client could then send in a confirmation email (and still stay secure)? The web service would still be checking the values such as name and password and then returning a 200 with a hash of an acceptance string. The client would then send the email with the exact contents you have in your final solution.

How to synchronize Client & Server ?

Hi mbinette,

> It would be difficult for the email to be customized properly (different "Thank you" message for each client).

I am also passing these messages from the Client Application to the Service. Actually, the client provides all resources the service needs to send the message. If I find time to blog about the implementation details, it will be more clear.

> The client would then send the email with the exact contents you have in your final solution.

The problem is: how do you assure the client sends the email after creating the account record in the database. In my current code, the both actions are executed in a same try-catch block, so if the SMTP fails, the JPA operations is rolled back, and I avoid the situation of having a new account in the database without the delivery of the confirmation email.

If you have this orphan record without the confirmation email, you are in a bad situation because the server will not accept a second registration request :)

But if you can guarantee client and server are in synch, no problem.

Undelivered confirmation

"The problem is: how do you assure the client sends the email after creating the account record in the database. In my current code, the both actions are executed in a same try-catch block, so if the SMTP fails, the JPA operations is rolled back, and I avoid the situation of having a new account in the database without the delivery of the confirmation email.

If you have this orphan record without the confirmation email, you are in a bad situation because the server will not accept a second registration request :)"

I guess I was thinking that if the client sent the request for a second registration and the account was not yet active, it would treat it as if it was the first time request, return a 200 with the same hash (or a 201 with the same hash?). That way the user who didn't get the email could retry without it adding new registrations and without it rejecting. Once the account has been accepted, trying to register again with the same email would fail.

humm.. good idea

Good idea,

I will adopt it :) For now, my code first check if the login or the email are already registered in the database and rejects the registration (HTTP CONFLICT).. This may lead a more safe relationship between client and server, and even unleash this use case of being done all on the server side.

But: there is a last argument to do it on the server side: to reuse the same code between the several clients consuming the service. A kind of "service resource". Otherwise, all clients will need to implement the email feature (or other confirmation feature). Since all clients will need that, perhaps it is a good reason to keep it as resource instead of required plugin :)

I liked your idea and I will implement it in my server side asap. Thanks.

great

Maybe the sending of an email should be a separate service? Then the client can take what is returned from the registration service and send it to the email service if they don't want to handle it themselves? Not sure but thanks again for the posts. They help me get out of my analysis paralysis ;) I look forward to your next post.

perhaps not a good idea :)

I reviewed my opinion about this.. and I started to think about the client application. It is not easy to handle all problems during the registration, and it gets worse if you do it in two different processes. So I rolled back my mind to the original solution: a unique transaction with "commit & email". To do the two operations in a unique transaction makes the whole use case simpler.

Reason: on the GUI we have two key fields in the registration form: login and email. As soon the user type the login, an AJAX function checks if the login is available on the server side. Assuming an end user can resend several times the same information sounds elegant but causes a side effect where I don't know what to do if a login already exists, or an email already exists or both. At first moment I thought about to update the information if the user type a same login & email but with different values in the other fields.. it is ok, but then I started to realize the cases where login or email do not match the database values.. and then we have a complicated permutation of values and a sophisticated code to support it on the GUI..

Due to that, I prefer to keep it as original designed: if a login or an email are already registered in the database, a new registration is not allowed. I will include a method to resend the email, just that - a separated one, because in my opinion it is a different use case (recover password).

open for suggestions on this topic.

OK

I think there is a difference in how you would handle an activated account and one that has not yet been activated. When you do your AJAX request to check a login (passing both the login name and email addresss, even if one is currently blank), the login name would be considered available until it has been activated. Then when an ajax call is made with the login name AND email, it would continue to say that combination is available if the combination matches what is in the database and it is not activated (the client can treat it as new even though it isn't). If it is already activated then it would say the login/email combination is already in use and it would make visible a "Recover Password" link. If one value matches and the other doesn't, the AJAX call returns that the login is not available. It would all be handled by the service so the client would not get overly complicated. It only needs to know whether the login is, based on the current information, valid, invalid, or exists already.

I don't really see an issue with either way of doing it and I don't think one is any better than the other. I think you might lean towards your final solution when you don't need to worry about branding too much or the possibility of multiple ways of activation from various clients. It simplifies the client, web service, and as a side affect, simplifies security as well.