bliako has asked for the wisdom of the Perl Monks concerning the following question:
Wise Monks+Monkees,
I have a Mojolicious Lite app which needs to place a cookie to client for session purposes, after successful login.
It works with Linux+Firefox as expected.
But on an updated/recent apple phone with Safari, it fails. It seems that it does not set the cookie to the browser. What I observe from looking at the phone (not mine) is that the login is successful. But upon redirection to the next page, which is "under" authentication condition and therefore checks if a cookie is set and a session is found, it behaves as if that page was accessed without first authenticating (e.g. "please login first").
This may well be irrelevant information but here it is: The app is hosted on a server with just a static IP. It has no 'domain' name. And it is hosted via nginx, along with other apps each on their own port. Each app is accessed like https://1.2.3.4/app1/login for app1, app2, etc. Nginx detects that, it removes the "app1" prefix and sends the request to the appropriate port WITH the added header "X-Request-Base". The app detects that header and passes it on to templates in order to prepend each reference to the app's url with the "app1" prefix. This works just great for all my apps. And I am mentioning it here just in case it makes a difference with Safari. Perhaps it freaks out seeing that extra header.
The other information is that I am using a self-signed SSL certificate (for said host). This means, I can access my apps via https://1.2.3.4/app1/login with Firefox and Chrome warning about "Warning: Potential Security Risk Ahead" (click "Advanced" and then "Accept the Risk and Continue"). But getting there eventually.
Safari warns about the Risk, goes to the Login page, but then it does not save the cookie when user credentials are correct. I know because it is redirected to the next page which is under authentication and says to go back and login properly.
Because, internally, my app does not use mix-mode HTTP[S], but *only* HTTPS, I am using "secure" cookies. Would that make a difference with Safari?
BTW, I do not have access to that Safari at this moment, so I can't answer anything else.
The test Mojo app below is run with MOJO_MODE=production hypnotoad myapp.pl and accessed on http://localhost:3000. PLEASE NOTE that it is HTTP-mode only. So that may cause it to succeed with Safari - please let me know.
The first time is run, the page shows that there is no session (no cookie/the user is not "logged in"/etc.) and it proceeds to create a session. The effect of which will be observed upon reloading which should tell you that it has received some stash-data from the server (which is stored in the session).
As I have no access to Safari at the moment, I don't know if this test app will exhibit the problem I encountered. But it is a a very slim version of how my real app is.
Also note that I don't set any third-party cookies neither I access any 3rdparty links from my app (even my CSS/JS are all stored on the same server with the app) EXCEPT an embeded google map on the page which is accessed after login authentication. That is, it is accessed only if the cookie is set! I have included the iframe in the test app below, one can uncomment it to test further.
Oh, my app uses Text::Xslate templating framework. But for simplicity I am here using Mojo's ep. One can uncomment a couple of lines below to switch to Xslate.
And here is the test app:
#!perl # MOJO_MODE=production hypnotoad myapp.pl use strict; use warnings; use Mojolicious::Lite; use Mojo::Log; # if you enable this then you need to set # render handler to 'tx', like this: # $c->render( ... 'handler' => 'tx' ...) #plugin 'xslate_renderer'; my $PORT = '3000'; my $COOKIENAME = 'cook'; my $log = Mojo::Log->new(); # make sure you set the secret because it defaults to the app's name!! +! # cookie name and secret must be different for different apps. app->secrets(['ajhgdauyda']); # secure cookies are those sent over HTTPS only # so if part of the app is over HTTP, the cookie will not be sent! app->log->level('info'); app->sessions->secure(1); app->sessions->cookie_name($COOKIENAME); $log->info("session cookie is '$COOKIENAME'."); # this is important if nginx is rewriting the url (e.g. handling multi +ple sites # it removes part of the url but we know the original and we must appe +nd it # on each request we make app->hook( before_dispatch => sub { my $c = shift; if ( my $base = $c->req->headers->header('X-Request-Base') ) { $log->info("found base '$base' and setting it ..."); my $u = Mojo::URL->new($base); $c->req->url->base($u); # this will go to the templates $c->stash('RequestBaseHeader' => Mojo::URL->new($u->to_abs +)); } else { $c->stash('RequestBaseHeader' => ''); } } ); ############################################################ # end of startup, start the routes ############################################################ get '/' => sub { my $c = $_[0]; if( exists($c->session->{'u'}) && defined($c->session->{'u'}) ){ # we have a session, read it and send it to the template # to print some content. $log->info("session found and sent to template ..."); $c->stash( 'd' => $c->session->{'u'} ); } else { # there is no session, set it $log->info("setting the session ..."); $c->session->{'u'} = {'hello' => 'hello i am session content'} +; $c->stash( 'd' => undef ); } $c->render( 'template' => 'checksession', # down in __DATA__ 'format' => 'html', 'handler' => 'ep', ); }; my $daemon = Mojo::Server::Daemon->new( app => app, listen => ["http://*:${PORT}"] ); $log->info("$0 : started listening on port ${PORT} and with mode ". ap +p->mode ." ..."); $daemon->run; #app->start; __DATA__ @@ checksession.html.tx <!doctype html> <html> <head></head> <body> <p> : if( $d == nil){ <p> No session yet, hopefully it has been set now! <p> Reload the page to see if it has been set. : } else { <p> You have session with this content: : for $d.keys() -> $k { '<: $k :>' => '<: $d[$k] :>' : } : } </body> </html> @@ checksession.html.ep <!doctype html> <html> <head></head> <body> % if( defined $d ){ <p> You have session with this content: % for my $k (keys %$d){ key='<%= $k =%>' = '<%= $d->{$k} =%>' % } <!-- the map: <div> <iframe src="https://www.google.com/maps/embed?pb=!1m14!1m12!1m3!1d828 +424.0952114153!2d9.034434313613875!3d40.04401928099502!2m3!1f0!2f0!3f +0!3m2!1i1024!2i768!4f13.1!5e0!3m2!1sen!2s!4v1731351850009!5m2!1sen!2s +" width="600" height="450" style="border:0;" allowfullscreen="" loadi +ng="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe> </div> --> % } else { <p> No session yet, hopefully it was set! <p> Reload the page to see if it has been set. % } </body> </html>
Edit: Thanks to the information provided by sectokia and karlgoethebier I have confirmed that Safari saves the cookie, in situations as above, when I do not use secure cookies but "plain", e.g. app->sessions->secure(0);. This is a quasi-solution because my settings, in this particular case, are for testing an app. Eventually the app will be migrated to host with "proper" certificates in the future. Thank you both!
bw, bliako
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Safari (apple) fails to set cookie from Mojo Lite app
by sectokia (Friar) on Nov 13, 2024 at 04:16 UTC | |
by karlgoethebier (Abbot) on Nov 13, 2024 at 12:07 UTC | |
by bliako (Abbot) on Nov 13, 2024 at 16:05 UTC | |
by sectokia (Friar) on Nov 14, 2024 at 02:35 UTC | |
by karlgoethebier (Abbot) on Nov 13, 2024 at 16:48 UTC | |
by sectokia (Friar) on Nov 14, 2024 at 02:39 UTC | |
by bliako (Abbot) on Nov 13, 2024 at 16:04 UTC | |
by bliako (Abbot) on Nov 18, 2024 at 11:16 UTC |