-
-
Notifications
You must be signed in to change notification settings - Fork 196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Identifying requests from one tab across refreshes and server-side pageloads #118
Comments
I realize now my original post was very wordy and maybe difficult to understand! My attempt at brevity: I just want to uniquely identify 1 tab in 1 browser on 1 device, but have that id persist across user refreshes or clicking links or anything which will prompt a second initial socket handshake within the same tab. How do I do that? o_O |
I have something like this working[1] in one of my projects. What I do is I add something like (GET "/init" req
(let [uid (or (get-in req [:session :uid])
(base64 8))]
{:status 200
:headers {"Content-Type" "text/javascript; charset=utf-8"}
:session (assoc (:session req) :uid uid)})) That way each time the page is loaded, that route gets run and if the session does not already contain a uid, it generates a new one. [1] This may not be quite what you want because, as its session based, if you have more than one tab, they will all get the same id. |
@danielytics thanks for the quick reply! :) And thanks for the code snippet! I was hoping for a completely stateless solution, but I think I'll have to use sessionStorage D: I can't think of any other way to isolate identity down to the tab level, but persist that identity across pageloads and refreshes. |
Hey Dillon, I'll quickly recap our earlier email exchange for others that might be following this or have similar questions. Sente currently uses two identifiers: a client-id and a user-id. Client id (determined client-side)A client is one particular ClojureScript invocation of Default client-id value: a random uuid generated when Notes:
Overriding default client-id value: User id (determined server-side)Sente supports async event pushing from the server (Clojure-side) to clients (ClojureScript-side) using the server-side So a user-id just specifies destination/s for async event pushing from the server. A user-id is determined server-side as: Default user-id value: Notes:
Overriding default user-id value: So the knobs you have to work with are: Between these two, you should have the control necessary to do just about anything you'd like. Some examplesYou want a per-tab transient user-id
I.e. we don't use sessions for anything. User-ids are equal to client-ids, which are random per-tab uuids. You want a per-tab transient user-id with session security
I.e. sessions (+ some kind of login procedure) are used to determine a You want a per-tab persistent user-id (i.e. that survives tab reloading)
This one's tricky since you'll need a way of persisting tab identity, and I'm not sure if browsers even have a concept of persistent tab identity or what that might look like. The first step would be to very clearly define what behaviour you're actually looking for and then Google around to see if the necessary info is actually available to browsers client-side. If it is, you can pass it along as your Something like HTML5 LocalStorage might be useful, but I haven't looked into the specifics to confirm. Hope some of that was useful, just let me know if I can help further clarify anything :-) Cheers! |
Hey Peter! Yes, I was planning on using either sessionStorage or localStorage to persist a uuid, and then setting the My initial hope was to find a completely stateless way to accomplish my goals, but I got some great info from here telling me that my ideal stateless method might not exist: ring-clojure/ring#197 Thanks so much!! 🍻 |
No problem, this question seems to come up often enough - will update the README to incl. the summary from here. Small thing to note: it's still not obvious to me how you'd use LocalStorage/SessionStorage to get the particular behaviour you want. The underlying difficulty is that (afaik) browsers lack a notion of tab identity. And this isn't a browser issue per se, it's just an intrinsically difficult thing to define. If you reload a tab, is it the same tab? What if you click a link in a tab, which takes you to a different page, then you eventually end up at the original URL again? What if you close a tab, then immediately open a new tab to the same URL? I'd start by deciding if/why you really need persistent tab-level identity. That may give you some clarity re: the very specific behaviour you want - then you can see if it'd be something possible/worth hacking together. Good luck! :-) |
is there a way to access client-ids inside one user-id? |
@altV, I do this a bit differently. I have a server generated UUID (you can use gensym too) for uid rather than any actual user information, and store each client's unique uid in a map with their real uid. Doing this I can send to one specific client, or loop over each uid a user has. Using this method I can handle authentication over a sente channel: (defn register-uid [state uid send-fn]
(assoc-in state [:clients uid :send-fn] (partial send-fn uid)))
(defn register-user [state uid user]
(-> state
(assoc-in [:sente :clients uid :user] user)
(assoc-in [:sente :connections (:uid user) uid]
(get-in state [:sente :clients uid :send-fn]))))
(defn deregister-user [state uid]
(if-let [user (get-in state [:clients uid :user])]
(-> state
(update-in [:sente :clients] dissoc uid)
(update-in [:sente :connections (:uid user)] dissoc uid))
(update state :clients dissoc uid)))
(defn auth [{:keys [send-fn uid ?data ?reply-fn store state] :as ev-msg}]
(if-let [user (first (store/query! store ["SELECT * FROM users WHERE name=$1" (:name ?data)]))]
(do
(if (= (get-in user [:data :password]) (:password ?data)) ;; Protip: don't store plain text passwords!
(do
(swap! state register-user)
(?reply-fn :xxx/login-ok))
(?reply-fn :xxx/login-failed)))
(?reply-fn :xxx/login-failed)))
(defn event-msg-handler [this {:keys [id] :as ev-msg}]
(tracef "Event: %s" (:event ev-msg))
(condp = id
:chsk/uidport-open
(swap! state register-uid uid send-fn)
:chsk/uidport-close
(swap! state deregister-user uid)
:chsk/ws-ping
(tracef "Ping from client: %s" uid)
:xxx/auth
(async/thread
(auth (assoc ev-msg :state state :store store))))) |
@theasp I like what you did there and would like to emulate your approach. Am I to assume that Also I can come up with an alternative but wanted to check my understanding with you. |
Hi @raymcdermott, My example was simplified from the actual code. |
Ok, cool - glad I’m not cracking up. You’ve shown enough to get the idea.
Thanks.
…On 21 March 2018 at 01:10:12, Andrew Phillips ***@***.***) wrote:
Hi @raymcdermott <https://github.com/raymcdermott>,
My example was simplified from the actual code. state would be an atom
for holding, and store is a db connection. You are correct, register-user
is missing arguments.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#118 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAHWdacU0FGdqAAe21-lKGOUETmYt1jDks5tgah0gaJpZM4D2fFB>
.
|
My problem: I want to know which "blarble" is associated with each client->server request, ideally as part of
ring-req
, but really as any part of the entire request map which comes in on the server'sch-recv
. Unfortunately I don't know and can't figure out how to do this.I made up the word "blarble" because I didn't know the correct word. To avoid all ambiguity and confusion, I decided to make up a word. I define a blarble as 1 tab in 1 browser on 1 device including all page refreshes and any traditional server-side page loads. If a user refreshes and the socket handshake is repeated, or the user clicks on a link which takes him/her to another server-side rendered page which prompts another socket handshake, then I want my server to still recognize all requests from all these separate handshakes as from the same single blarble since they still came from the same tab.
I want to contrast blarbles with Sente clients (1 tab in 1 browser on 1 device, but every refresh in the same tab is a new client) and Sente sessions (1+ tabs in 1 browser on 1 device, but these refresh after every refresh as well).
What can I do to make my server recognize blarbles?
Disclaimer 1: I'm relatively new to both clojure and back-end web development, so I totally realize the answer might be trivial and I'm just missing it.
Disclaimer 2: I realize that the answer might not be within Sente and rather within Ring or (maybe less likely) Compojure.
The text was updated successfully, but these errors were encountered: