Project

General

Profile

Actions

Support #12843

closed

Remember me cookies

Added by Andrey Alekseev about 2 months ago. Updated about 1 month ago.

Status:
Rejected
Priority:
Normal
Assignee:
-
Target version:
-
Start date:
07/21/2024
Due date:
% Done:

0%

Estimated time:

Description

In my project I have:

  • PWA, which is implemented by plain HTML file and a WidgetSet entry point that hase some JSlots and uses Auth (binds an AuthWidget to the page)
  • WResource for HTTP API, which helps me for caching some data.

I needed to determine current authenticated user while processing requests. I found out that I can use remember-me cookies for that:
WApplication and WResource are hosted with same path prefix (/client/):

server.addEntryPoint( EntryPointType::WidgetSet,
    [&connPool](const WEnvironment& env){return make_unique<ClientApp>( env, connPool.get() );},
    "/client"
);

server.addResource( make_shared<ApiResourse>(connPool.get()), "/client/api/${data}");

When user logs in, login cookie is saved and it applies to all requests with /client path prefix

And now I can identify current user in WResource like this:

void ApiResourse::handleRequest(const Http::Request &request,
                                Http::Response &response) {
    if ( request.urlParam("data").empty() ) {
        response.setStatus(400);
        log("api") << "no parameters";
        return;
    }

    const string *token = request.getCookieValue(s.auth().authTokenCookieName());
    if ( !token ) {
        log("api") << "empty token";
        response.setStatus(401);
        return;
    }

    Auth::User authUser = s.users().findWithAuthToken(s.auth().tokenHashFunction()->compute(*token, string{}));
    if ( ! authUser.isValid() ) {
        log("api") << "user not found";
        response.setStatus(401);
        return;
    }

    Dbo::Transaction t(s);
    UserPtr user = s.users().find(authUser)->user();
    user.reread();
    response.setMimeType("application/json");
    try {
        (*this.*endpoints.at(request.urlParam("data")))
            (user, request, response);
    } catch ( out_of_range e ) { response.setStatus(404); }
}

The problem is that cookie is present only after a page refresh, so I had to add a small script, which refreshes page after login.
Is there a way to make cookie appear immediately after login without refreshing the page?


Files

cookies.png (211 KB) cookies.png Andrey Alekseev, 07/21/2024 12:56 PM
Actions #1

Updated by Andrey Alekseev about 2 months ago

I got it.
It happens because request related to JSlots are sent firstly for some reason, and after that I see request, related to authentication, which send us a login cookie.
I added a small delay with WTimer and now client app requests my WResource exdactly after login cookie is present

Actions #2

Updated by Matthias Van Ceulebroeck about 2 months ago

  • Status changed from New to Rejected

I'm happy to hear you resolved the issue yourself.

I believe this may be related to #12022 or #11669 (not directly, but the underlying idea of it). JSlot data is sent together with the rest of the widget set-up. I am assuming then that the widget is set up before the response request is fully parsed by the browser.
Is the WTimer stable enough to rely upon? Maybe you can send over some JS with the response that executes JS that would request the WResource. Of course if the timer is good enough, carry on!

P.S. the rejected state is to indicate no code was implemented for the ticket.

Actions #3

Updated by Andrey Alekseev about 2 months ago

I just tried disabling WebSockets in Wt config and I can't reproduce this anymore. In web inspector I see login-cookie request happens before widget updates.

After enabling WS back:

  1. WS message, containing js with page changes (including my JSlot call), and only at the bottom - the following line
Wt._p_.setFormObjects([]);Wt._p_.refreshCookie();Wt._p_.wsRqsDone(0);
  1. a request to WResource (which is faled with 401)
  2. a request to WidgetSet - response has "Set-Cookie" header with logincookie contents

I believe that Wt._p_.refreshCookie() needs to be at the beginning of the server response, so Wt can have same behavior with AJAX and WebSockets
Otherwise the solution is of course to wrap my JSlot call into another js, so for WebSockets there will be separate messages with correct events order

What do you think? Should we open a bug, or my use of login-cookies is not what they were designed for?

Actions #4

Updated by Andrey Alekseev about 2 months ago

I just looked into Wt's js. I was wrong. The reason is Wt._p_.refreshCookie(), like some other finctions, just creates a usual async request, so no matter where it placed - the last request will be most likely sent before the browser gets a response for the fist one.
So I guess with AJAX we can see same picture, but with more delay for some reason.

Actions #5

Updated by Wim Dumon about 1 month ago

Hello Andrey,

the login cookies were intended for the remember me function only. The system that is included in Wt for the security of WResources, is to use session-private resources, i.e. the toolkit will generate the proper URL for your resources, including a session secret (and optionally a different cookie, depending on wt_config settings) that links the resource to the proper session. Would you be able to use that mechanism?

Wim.

Actions #6

Updated by Andrey Alekseev about 1 month ago

The system that is included in Wt for the security of WResources, is to use session-private resources, i.e. the toolkit will generate the proper URL for your resources, including a session secret (and optionally a different cookie, depending on wt_config settings) that links the resource to the proper session. Would you be able to use that mechanism?

Sounds great
But if WResourse that bound to an application may have different urls every session - I will need to use a different approach for data storage, because I've implemented it using ServiceWorker's Cache API
I think I will try it in my next project

Actions

Also available in: Atom PDF