Web Development with Clojure, Third Edition: bug in Restrict Posting to Authenticated Users? (page 194)

@Dmitri @svmbrown

At the end of Restrict Posting to Authenticated Users, I think there’s a bug but I’m not able to find any solution.

Consider this scenario, an user loads the page without logging in, then after staying on the same page, logging in and trying to post will leads to error "Please log in before posting". I’m able to see that it’s the direct result of websocket.clj returning {:unauthorized true} to client and it triggers the :form/set-server-errors.

Does this have anything to do with open websocket remains unauthenticated after browser successfully visits /login?

That bug should be addressed by the custom session store we introduced on page 187.

We exposed access to the underlying ttl-memory-store so that we could access up-to-date authentication information in it from the websocket handler. This is because our websocket only has the initial HTTP request sent on connection, so the session information that would be unpacked by the default middleware would be stale.

I’m now onto page 244 and this issue is still there.

The way to reproduce in original post no longer works. Here’s a different way to reproduce:

  1. login as user1
  2. logout
  3. login as user2
  4. try to add a message

I think I might know what it is, but I can’t reproduce it at the moment because I don’t have the full codebase handy on this computer. Could you try changing the /logout handler in guestbook.routes.services to set the session to an empty map instead of nil like so?

["/logout"
    {:post {:handler
            (fn [_]
              (->
               (response/ok)
               (assoc :session {})))}}]

I believe that setting it to nil is completely blowing away the ttl-memory-store so that we end up with a new session ID when we hit /login again, but the websocket still has the stale session ID.

If this is the problem, the “proper” way to solve it would be to keep the nil server-side, but to have the client refresh/redirect to / so that all client-side state is re-instantiated as well.

Problem solved. Empty map {} does the trick. Thank you.