captive portal is not working on mobiles
-
Look at DHCP option 114 described by Apple at https://developer.apple.com/news/?id=q78sq5rv . Note that:
You will need a secure link to the captive portal, i.e. an ssl certificate for pfSense captive portal that is resolvable by the device, iOS, Android, Windows, etc. Thus, a self-signed certificate will not work and your DNS must pass through the firewall so they can resolve it.
You will need to create a custom DHCP option 114, type= string, value= "https://vlanname.pfsense_ssl_cert_name.com:8007/dhcp_cp3.php?zone=vlan30". Kea does not yet support this in 2.7.2/23.9.1 so use ICE
The file dhcp_cp3.php is a file that checks to see if there is a captive portal session active, it includes something like:
Note: look at index.php for an example of how to read/access session variables. All files are in /usr/local/captiveportal/
.................................................
$cpsession = captiveportal_isip_logged($clientip);/* False /
if (!empty($cpsession)) {
$sessionid = $cpsession['sessionid'];
ob_flush();
header("Location: https://vlanname.pfsense_ssl_cert_name.com:8007/dhcp_cp3_F.php?zone=vlan30");
} else {
/ True */
ob_flush();
header("Location: https://vlanname.pfsense_ssl_cert_name.com:8007/dhcp_cp3_T.php?zone=vlan30");
}
.......................................................The true and false files contain the java data that is sent to the device to tell it if/how/where to log onto the captive portal:
{
"captive": true,
"user-portal-url": "https://vlanname.pfsense_ssl_cert_name:8007/index.php?zone=vlan30"
}
........................................................Hints:
If you use multiple vlans, you will need a separate sub domain for each one. I use cloudflare as it provides free first level sub domains. I point the DNS to the appropriate dhcp address associated with the captive portal. eg: vlan1.pfsense_ssl_cert_name.com which is on 192.168.10.1, vlan2.pfsense_ssl_cert_name.com which is on 192.168.20.1, etc. All devices will be able to resolve the certificate if they have an address inside the pfSense captive portal and thus meet the operating system requirement for a secure connection. Obviously the captive portal must be associated with the same vlan for this to work. The captive portal "port" for secure connections is vlan1 at 8003, vlan2 at 8005, vlan3 st 8007, etc. if you assign them in sequence.I have been using this for the last two years with perfect results.
-
@EDaleH said in captive portal is not working on mobiles:
https://developer.apple.com/news/?id=q78sq5rv
Thanks for the info.
I've been looking at this "How to modernize your captive network" page for long time, biu never actually took the steps to implement it.
Adding the DHCP 114" option is easy, adding the file "dhcp_cp3.php" is also pretty straight forward.Do I get this right : I could use the Services > Captive Portal > ZONEname > File Manager in the GUI to upload the "dhcp_cp3.php" file, it would be called "captiveportal-dhcp_cp3.php" in the /usr/local/captiveportal folder.
Change the "DHCP 114" URL accordingly - and all that needs to be done manually is editing the index.php file - right ?At this moment, I've done nothing the like, as soon as I select my captive portal with my iPhone, after a couple of seconds, the captive portal login page splashes open, and I can enter the portal credentials.
Yesterday, I had to the small ones to their favorite restaurant (McDondalds) and I saw that my phone showed some sort of popup message that "A open (free ?)" wireless was available..... wonder how they did this, at it quiet user friendly.I'll try your method, but first : weekend.
-
Not quite, all of the file uploading I personally do through the Diagnostics, edit file option. I browse to /usr/local/captiveportal/ and open any file, change it's name to the one I want, empty it and then paste in my code, save. There are many other ways to do that too. So, once you upload the dhcp_cp3.php file, it is now available to the DHCP server custom option 114 when the device first connects to the pfSense firewall and gets an IP from the subnet assigned by DHCP and associated with that portal, there is nothing to do in the captive portal at all, none of what I listed does. All Captive portal is involved with is assigning the vlan so you log them into the correct instance/vlan and not a different one. When the device connects, it is looking for custom option 114 and thus, it receives one of the data streams based on the logic in dhcp_cp3.php. The device then opens the appropriate page, for example:
{
"captive": true,
"user-portal-url": "https://vlanname.pfsense_ssl_cert_name.com:8007/index.php?zone=vlan30" .... Note the .com was missing in my original above.
}If you try this with an http:// link it will fail on apple and android devices as they have implemented SSL/TLS only for logins as of a few years ago. The old approach of loading something like http://neverssl.com is no longer reliable and will fail most of the time.
Thus, the device loads the captive portal page instead of typically looking for the web site it uses to verify connectivity, such as apple.com for eg. This avoids the "no internet" message due to the captive portal blocking access. You might think you could just pass that site through but then the device will think it has internet connectivity when it doesn't and all "heck" will break loose for the poor user as there is no way they will know the link to log into the captive portal to get access. The default http:// link is not secure and will fail and of course a self-signed certificate will not be available to the device so it must be done in association with a valid, registered domain with subdomains for more than one captive portal. The DNS simply points to the correct captive portal network, it does not have to be a public address as we don't want to access it from outside the firewall anyway.
Below is the entire dhcp_cp3.php file which I butchered index.php to create. After you finish laughing, you can clean it up.
.................................................
<?php
/*- index.php modified to check if captive and return JSON string for DHCP 114
- part of pfSense (https://www.pfsense.org)
- Copyright (c) 2004-2013 BSD Perimeter
- Copyright (c) 2013-2016 Electric Sheep Fencing
- Copyright (c) 2014-2022 Rubicon Communications, LLC (Netgate)
- All rights reserved.
- Originally part of m0n0wall (http://m0n0.ch/wall)
- Copyright (c) 2003-2006 Manuel Kasper mk@neon1.net.
- All rights reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
*/
require_once("auth.inc");
require_once("util.inc");
require_once("functions.inc");
require_once("captiveportal.inc");header("Expires: 0");
header("Cache-Control: no-cache, no-store, must-revalidate");
header("Pragma: no-cache");
header("Connection: close");global $g, $config, $cpzone, $cpzoneid, $cpzoneprefix;
$cpzone = strtolower($_REQUEST['zone']);
$cpcfg = config_get_path("captiveportal/{$cpzone}");/* NOTE: IE 8/9 is buggy and that is why this is needed */
$orig_request = trim($_REQUEST['redirurl'], " /");/* If the post-auth redirect is set, always use it. Otherwise take what was supplied in URL. /
if (!empty($cpcfg) && is_URL($cpcfg['redirurl'], true)) {
$redirurl = $cpcfg['redirurl'];
} elseif (preg_match("/redirurl=(.)/", $orig_request, $matches)) {
$redirurl = urldecode($matches[1]);
} elseif ($_REQUEST['redirurl']) {
$redirurl = $_REQUEST['redirurl'];
}
/* Sanity check: If the redirect target is not a URL, do not attempt to use it like one. */
if (!is_URL(urldecode($redirurl), true)) {
$redirurl = "";
}if (empty($cpcfg)) {
log_error("Submission to captiveportal with unknown parameter zone: " . htmlspecialchars($cpzone));
portal_reply_page($redirurl, "error", gettext("Internal error"));
ob_flush();
return;
}$cpzoneid = $cpcfg['zoneid'];
$cpzoneprefix = CPPREFIX . $cpzoneid;
$orig_host = $_SERVER['HTTP_HOST'];
$clientip = $_SERVER['REMOTE_ADDR'];if (!$clientip) {
/* not good - bail out */
log_error("Zone: {$cpzone} - Captive portal could not determine client's IP address.");
$errormsg = gettext("An error occurred. Please check the system logs for more information.");
portal_reply_page($redirurl, "error", $errormsg);
ob_flush();
return;
}$ourhostname = portal_hostname_from_client_ip($clientip);
$protocol = (isset($config['captiveportal'][$cpzone]['httpslogin'])) ? 'https://' : 'http://';
$logouturl = "{$protocol}{$ourhostname}/";$cpsession = captiveportal_isip_logged($clientip);
if (!empty($cpsession)) {
$sessionid = $cpsession['sessionid'];
ob_flush();
header("Location: https://vlanname.pfsense_ssl_cert_name.com.8007/dhcp_cp3_F.php?zone=vlan30");
} else {
ob_flush();
header("Location: https://week.wifi-nsrv.com:8007/dhcp_cp3_T.php?zone=vlan30");
}ob_flush();
return;?>
.................................................
I also use dhcp custom option 114 to pop up a message on unauthenticated captive portals to have users agree to a user agreement without technically logging in. I am pretty sure Macdonalds used option 114 to pop up your message. You are in complete control to replace "index.php" (under a new name, don't kill the original!) with any custom code you want. It will pop up on the device at the same time the IP address is assigned, it has nothing to do with providing internet access, that is the job of the captive portal. The device will only check for internet connectivity once you return control to the custom browser they use for logging into captive portals. If there is no connectivity, it will still display the "no internet connection" prompt.I hope that helps.
-
You should also enter host overrides in the DNS Resolver for all captive portal IP ranges assigned in DHCP, There will be one for each captive portal and one set of files mentioned above for each captive portal. All must match!
eg:
host: vlanname
Parent or Domain of Host: pfsense_ssl_cert_name.com
IP to return for host: 192.168.30.1.This will ensure the certificate can be associated with the correct address if the DNS is not resolved at the time of login.
-
@EDaleH said in captive portal is not working on mobiles:
host: vlanname
Parent or Domain of Host: pfsense_ssl_cert_name.com
IP to return for host: 192.168.30.1.Yep, not something one can forget as it is part of the mandatory Netgate "captive portal' video's, somewhere on this page
@EDaleH said in captive portal is not working on mobiles:
If you try this with an http:// link
Why would I even try ?
The OS 'any OS) will throw out a "connection test" http request (not https !).
For example, my iPhone uses : "http%3A%2F%2Fcaptive.apple.com%2Fhotspot-detect.html" (you find it in on the Status > System Logs > System > GUI Service page).If the page shows up with a known answer (= "Success "), the device will know it has a direct internet connection, and every thing stops there.
Ok, true, all it knows is that http requests (TCP to port 80) work - and because captive.apple.com resolved, DNS also work).If nothing or something else comes back, the OS fires up a web browser (a scaled down version of Safari in Apple's case) - or your default system browser ion other devices wit the same ( ! ) http://captive.apple.com/hotspot-detect.html URL.
Because of the web server's redirection instructions,
and pfSense 'pf' firewall rues ( see /tmp/rules.debug) the https request will get redirected to
http://192.168.2.1:8002/...... if you've chosen http login, or
https://portal.yourlocaldomain.tld:8003/ if you have chosen https login.And yes, as you are already aware, "portal.yourlocaldomain.tld" needs to be resolved locally (the pfsense resolver), as a valid (trusted) certificate needs to be put in place, on the captive portal's web server. That's where the acme pfSense package comes in handy : set it up ones and never come back.
http, and since 2015 (?) https portal login always worked fine for me.
My laptop PC, since .... before Windows 7; then 8, then 10 and now 11 (always the pro version) also always worked fine (having FF as the default browser).
Maybe android users have problems ones in a while ..... I'm not sure. Nobody is complaining for more the a year now, people (hotel clients !!!) are connecting just fine with whatever they bring along with them.Btw : I presume you know all this. Clear is : you know something I didn't know(use) yet.
I'll try things out tomorrow when on site.
Editing with the GUI ? No way - SFTP + Notepad++ me. Or SSH into the box and "vi" (ed, nano) if I have to. -
For those whom aren't familiar with how to get the SSL certificate to associate with your registered domain (pfsense_ssl_cert_name.com in the above example), look at the acme package. You will have to register all sub domains like vlanname.pfsense_ssl_cert_name.com on the certificate so each can resolve (thru DNS) to the matching subnet for that captive portal.
Summary of steps required to support DHCP 114 on pfSense Captive Portal:
- Register Domain with a registrar that supports "free?" first level sub domains (pfsense_ssl_cert_name.com in eg. above)
- Create first level sub domains at the registrar/DNS for each portal (vlanname in eg. above) and point the DNS at your local subnet/vlan (192.168.30.1 for eg above)
- Optionally make the host override entries for each in pfSense DNS Resolver
- use the acme Package to register SSL certificates (in the acme Domain SAN list there will be one entry for each sub domain)
- issue/renew the certificate in acme and the default 60 day autorenew will take care of the rest.
- Setup Captive portal with
- HTTPS Server name (vlanname.pfsense_ssl_cert_name.com in eg. above)
- SSL/TLS Certificate (pfsense_ssl_cert_name.com in eg above)
- Setup DHCP with option 114 configured for the subnet supporting this Captive Portal (192.168.30.1 in eg above)
- On your access points, setup Station IDs associated with the vlan ID for that subdomain (30 in above eg.) for each Captive Portal
When the device connects to that Station ID (vlanname), pfSense will assign an address thru DHCP that includes the 114 option which will then validate the session ID and send the appropriate data stream to the device to identify that it is "Captive = True" and where to go to get the login screen. (Location: https://vlanname.pfsense_ssl_cert_name.com:8007/dhcp_cp3_F.php?zone=vlan30"). The device will confirm the https session by verifying the SSL cert for "vlanname.pfsense_ssl_cert_name.com" and launch it's dedicated browser for logins, then load that login page. When the login page is submitted, and access granted through pfSense, the device will have internet access and will confirm that for itself. On most devices the custom browser will remain on the device's screen but close as soon as you leave that instance to go to another on the device. It would be polite to send some info to the end user so this screen is meaningful, i.e. the logout page in captive portal which will typically disappear the instant they go to use another app on their device.
Note: If pfSense indicates that that the device is already authenticated, it will send the alternate "Captive = False" in DHCP 114, the device will not load the login screen and will use it's normal connectivity validation process to confirm connectivity which will be invisible to your user unless, of course, there is no internet connectivity. This means the device will simply connect without a login screen until it is logged out of the captive portal.
-
@Gertjan
Well, there are many version of android and iOS out there and many devices that aren't up to date. Windows has been fairly reliable with proper launching of the login screens but we see thousands of devices a season and the support of the "basic" implementation you suggest was not reliable prior to 2.7.0 or 23.05. Starting with iOS 14, I believe, the secure session became mandatory and basically many devices simply don't log in. Support was a nightmare with playing around with http logins like neverssl.com or apple.com and attempting to "trigger" the login screen, often failing entirely to do so.Implementation of the steps I suggested has resulted in No login issues for the last 2 seasons. I would not know if 2.7.2 or 23.09.1 resolved the problem since and works for all device firmware versions. it just works and to me at least, makes sense.
-
@EDaleH
What you are saying, and what I still see today Apple devices are unable to load the portal page or login, it never made sense to me.
I can't have been lucky all these years - Am I ? And not only me, also my hotel clients ....What Apple (iOS) issues ? I've probably missed them all, as the slightest iOS portal detection issue would make the 100....000 users base complain, and Apple would have solved it the very next day with world wide iOS flash update. This is probably one of the advantages of using a device with a centralized OS development.
And I really tried to make make the portal 'fail', as I wanted to make to portal work for "everyone". Knowing up front any potential issues are is important for me.
I've always been a fan of the KISS approach : use a dedicated LAN interface for the portal.
Set up the portal as jimp showed, his videos, many years ago, and it will work. These videos are still valid and usable today.
And yes, for https login to work, a domain name is needed, which means an annual fee of about, what ? 10 $/โฌ ? a year.@EDaleH said in captive portal is not working on mobiles:
DHCP 114
I fully agree that that method make the device be aware that a portal is in play right after the mandatory DHCP client request, and the pfSense part of the portal support could be brought back to a strict minimal.
-
@Gertjan
All I can tell you is that I estimate that this site, that I set up 15 years ago, on pfSense sees about 200 devices connected at any single time over a 6 month seasonal period in a year. People and devices are constantly coming and going so there is a wide range of opportunity to see many different configurations. There are a wide variety of countries represented as well, increasing the device diversity. It has a mix of every type of authentication method, including freeRadius, vouchers and Local Database. In 2021, perhaps 1 in 10 devices would have issues and took a power cycling to correct 50% of the time. In 2022 I implemented my first use of DHCP114 but the example from Apple had some missing quotes in it and it took most of the season for me to figure that out as we still had random failures to connect. Android was less trouble but still had occasional issues. Let's remember that iOS was still mastering their side as well and each iOS release changed the symptoms a bit. By the end of 2022, the sample I recently provided here was complete and we did not have a single login issue during the 2023 season, mostly under 23.05/2.7.0. In fact there is no one dedicated to support Captive Portal on site at all. I consider DHCP 114 to be an essential contributor to that improvement. It is not all that complex to implement. The fact Kea DHCP doesn't yet support options is the next hurdle to get across but I am optimistic that this is a long term fix. We have other, massive, captive portal issues with data quotas, time quotas, multiuser data/time tracking and Vlan Tunnel support for freeRadius and we use custom software mods to fix all those successfully. Redmines have been raised (locked onto "NEXT" so never coming?) but the one for the freeRadius Vlan Tunnel support was outright cancelled so I expect it will be a very long time before the Captive Portal works for us without customization. The fact we have to customize captive portal forced us to downgrade from plus to 2.7.0 going forward to reduce the frequency of having to update our code to the latest version of pfSense. We are currently on 2.7.2 after experiencing an offseason data crash that seems eerily similar to the published ZFS file system issue.At least we feel the login issue is behind us and I thought others might benefit as well so I posted here.
-
@Gertjan said in captive portal is not working on mobiles:
Why would I even try ?
The OS 'any OS) will throw out a "connection test" http request (not https !).
For example, my iPhone uses : "http%3A%2F%2Fcaptive.apple.com%2Fhotspot-detect.html" (you find it in on the Status > System Logs > System > GUI Service page).If the page shows up with a known answer (= "Success "), the device will know it has a direct internet connection, and every thing stops there.
Ok, true, all it knows is that http requests (TCP to port 80) work - and because captive.apple.com resolved, DNS also work).If nothing or something else comes back, the OS fires up a web browser (a scaled down version of Safari in Apple's case) - or your default system browser ion other devices wit the same ( ! ) http://captive.apple.com/hotspot-detect.html URL.
In an attempt to answer this and explain why I prefer the DHCP 114 approach along with explaining what I believe may be happening:
- It takes time for everything to process, wait for a response, check to see if it says "Success", timeouts and sometimes the word "Success" is so small on the temp browser screen that users can't see it at all, thinking the connection process has hung. There is also a delay before the connection screen clears the "orange" No Internet Connection prompt. End users can miss all this and simply try to connect again.
- Another issue I encountered is that the same URL can resolve to multiple IPs over time on many sites. The pre-fetch on the Resolver can have a different, wrong IP address and that can cause issues. This is true for pass through URLs on the Captive Portal as well.
- The assumption that the device connection test always goes to the same URL is incorrect. There are dozens or hundreds of different URLs that are checked. I found that was one of the reasons for it being intermittent, especially if it couldn't be resolved through the portal. I found it retries different sites randomly to attempt to verify connectivity if routing to the first one is down. You are relying on this fixed URL to launch the rule for redirect to captive portal login as well so if it checks a different one, it fails to launch the login screen. And then there is android and all the other operating systems out there.
- Using DHCP custom option114 was created and promoted by Apple but adopted industry wide and will work immediately every time. No need for the firewall rules or delays in letting the "OLD" system of checking for "Success" to work. I must admit that there can still be a delay after captive portal authentication when iOS checks again for connectivity but it is a delay in removing the "No Internet Connection" display, the connectivity itself is instantaneous and I often see an email arrive before the Apple device removes that warning.
I think you may have more patience than some and often it is just the user of the device not understanding they are connected or not current in their device updates. If DHCP 114 fails, the system you rely on is still there as a "Backup" and that fact is what made debugging it so time consuming.
The universal nature of using DHCP option 114 is faster and more reliable than the "assumptions" and delays built into the rules bases setup you describe.
-
Also look at: https://datatracker.ietf.org/doc/rfc8910/ for info on DHCP 114 specs
-
To make this discussion complete and to provide a diagnostic process to test if Captive Portal is working in response to this topic itself:
@Gertjan said in captive portal is not working on mobiles:
"Because of the web server's redirection instructions,
and pfSense 'pf' firewall rues ( see /tmp/rules.debug) the https request will get redirected to
http://192.168.2.1:8002/...... if you've chosen http login, or
https://portal.yourlocaldomain.tld:8003/ if you have chosen https login."- The reason the device sends an http:// URL to captive portal, after failing to receive a "Success" response, rather than an https:// URL to the captive portal is because the captive portal can redirect http:// simply because it is not encrypted. Any https:// URL would not be intercepted because the captive portal/firewall would not be able to decode it into a url to recognize/resolve and redirect to the login page. One exception is the https://portal.yourlocaldomain.tld:8003/.... URL of the login screen itself as it does not need to be redirected at all.
- Likewise, the reason http://neverssl.com will still work in a device browser and will almost always launch the login screen is because it can be decoded and redirected by the captive portal/rules to the captive portal login page. This is not always true of the temp browser launched by the operating system of the device, iOS for eg, it may require a secure login. In that case, close the temp browser, open another browser like safari, and enter http://neverssl.com and it will launch the login screen if captive portal is set up correctly.
The most reliable fix is to "forget" the wifi station and then reconnect to the Access Point. Most often that corrects the login issue. If it does not, then exiting the temp browser, "Done, use without internet" option or just bailing out, and using something like Safari or Chrome with a url of http://neverssl.com, or other http:// URL, will launch the login screen and captive portal can then activate the session. If it does not, then captive portal setup may be incomplete.
In my experience, this is less likely to happen if iOS has access to valid DHCP option 114 data. This can happen even if you are using DHCP 114 because bailing out of the login screen without responding can confuse iOS and Captive Portal if the wifi connection remains intact.
-
@EDaleH said in captive portal is not working on mobiles:
In an attempt to answer this and explain why I prefer the DHCP 114 approach ...
When I found Apple's suggestion, the proposed RFC solution you've showed above, I knew that that was the way to go.
My thoughts are this : DHCP negotiation telling the device that a portal is present. It knows right from the network connection start what URL to present to the user so he/she can login.
Just perfect. It can be https right away.@EDaleH said in captive portal is not working on mobiles:
check to see if it says "Success", timeouts and sometimes the word "Success" is so small on the temp browser screen that users can't see it at all
The user can't see success ??
The user never should see that word. The test URL is send right after DHCP negotiation, typically by a command line like 'lynx'.
I need two command on a pfSEnse to do this manually :[23.09.1-RELEASE][root@pfSense.bhf.tld]/root: fetch http://captive.apple.com/hotspot-detect.html hotspot-detect.html 69 B 437 kBps 00s [23.09.1-RELEASE][root@pfSense.bhf.tld]/root: cat hotspot-detect.html <HTML><HEAD><TITLE>Success</TITLE></HEAD><BODY>Success</BODY></HTML>
An example on a Debian host :
root@ns311465:~# lynx -dump http://captive.apple.com/hotspot-detect.html Success
@EDaleH said in captive portal is not working on mobiles:
It takes time for everything to process, wait for a response, .... delays ....
I've read (forum) about delays.
My captive portal has 20 max connected users so my 4100 can handle the load quiet easily.
Btw : I've 4 access points in my portal network.@EDaleH said in captive portal is not working on mobiles:
Another issue I encountered is that the same URL can resolve to multiple IPs over time on many sites.
The IP resolved to doesn't matter.
If a portal is present, the http (TCP port 80) request gets redirect to the pfSense portal web server.@EDaleH said in captive portal is not working on mobiles:
The assumption that the device connection test always goes to the same URL is incorrect.
My device, over the year : a a 3G, GS, a 4, 5 7 10 and now iPhone 12, using whatever iOS available during there life span, did.
But, what http test site is used doesn't matter. It should be a http request and that URL should give a known answer back, that's all that counts.@EDaleH said in captive portal is not working on mobiles:
I found that was one of the reasons for it being intermittent, especially if it couldn't be resolved through the portal
That's bad : for a portal to work, DNS should work.
If the device's OS uses a test URL that doesn't exist (isn't resolvable - doesn't have a web server behind it answers "Success" or whatever it should answer) .... well .... then you have a device with a questionable OS. The OS would break captive portal support.
I can image that when you buy a no-brand phone with a cloned android for that device, and the portal detection URL is taken down then for that device portal detection breaks ...
In that case, the issue isn't on my side (the pfSense captive portal), neither the user. "They have what they paid for".
I'm don't want to be that iPhone fan boy, but I never had issues with their devices/OS.@EDaleH said in captive portal is not working on mobiles:
You are relying on this fixed URL to launch the rule for redirect to captive portal login as well so if it checks a different one
Again : the url used isn't hard coded in pfSense. It doesn't care.
No where I've "http://captive.apple.com/hotspot-detect.html" in my pfSense portal settings.
It is hard coded in my phone, that's for sure. Maybe it can fall back to other URLS, I don't know, never saw them.What I've also have seen : Microsoft OS device use a couple of different http URLs.
Android devices users other URL's of course, some are going to a google owned site, some go straight to China .... all depends on the device's OS version an whatever.@EDaleH said in captive portal is not working on mobiles:
Using DHCP custom option114
It has also a IPv6 DHCP solution !
The pfSense portal is IPv4 only right now. The DHCP solution will add full IPv6 support, something I've tried adding myself several times. It was mission impossible. -
I agree that when nothing goes wrong, the login screen will launch and any http:// URL will launch the login screen. The problem is that the real world user does not know that and they "mess with it".
A successful login screen involves both Captive Portal and the Device, so, let's look at it from the Device's perspective:
One common example: A user lands at your installation with an iPhone and looks around. They find a bunch of captive portals available, Day, Week, Month, Recurring, Data, etc. So they try them and connect to each in turn and when the login launches they seem to be stuck on the screen so they make the only choice they have, (Done or Cancel depending upon if this is the first time or they have been here before) they select either Done which closes the browser and typically iOS displays "No Internet Connection" but not always; or, they select "Cancel" which will prompt them to "Use Other Network" or to "Use Without Internet". In many cases they select to use it without internet. Both Done and "Use Without Internet" have turned off the "Auto Login" feature so the next time they connect to this network, it won't launch the login screen simply because it doesn't check for internet connectivity and thus does not try to access an http:// site, therefore failing to trigger the Captive Portal login page. Now they walk up to the Owner and say, the WiFi is not working and the inexperienced Owner of the facility contacts you and says the Captive Portal is broken. The severity of this varies and the current iOS 17+ has yet again improved and this "self corrects" more often, but it still happens, I just duplicated it on an iPhone 12 iOS 17.2.1 while writing this response. Also of note is that iOS does not always display the "No Internet Connection" warning, (because it is no longer checking?) it often just shows a connection so there is no feedback as to what is wrong. If they try to access a secure site like www.google.com, it will display security messages that make users uncomfortable. Those https:// sites do not trigger the captive portal login page, only an http:// link will. The problem is that as long as that WiFi Station ID is in the "My Networks" list, the Auto Login feature will remain off and there will be no Login page when they connect.
Now this broken Captive Portal/Phone needs to be fixed: What do you do next.
- Launch Safari and go to an http:// site (http://neverssl.com) to see if the captive portal launches the login screen and voila, it does. It also will leave the logout window/tab open in the browser if you need to enable your users to logout. The temp browser on the other hand will display but will close the logout window. when you leave the browser to use the phone.
- Tell them to forget the network and reconnect. This will clear the "Auto Login" setting so they see the login screen one more time. The is the quick and dirty solution to put into your handouts.
- Tell them to turn the "Auto Login" feature back on.
As far as issues with the DNS Resolver, pre-fetch, etc, they tend to affect the end user when they are trying to access secure URLs without authentication through the Captive Portal. This is especially true for payment systems if your login process includes an online payment link that must be redirected to before completing the pfSense login (voucher sent automatically to login screen for eg.). All of those links must get through the firewall to work and they are the IPs that are constantly changing. When an user bails out of the payment system, it produces the same results as bailing out of the temp browser login screen does.
Having iOS display the word "Success" in the temporary browser rather than the login screen occurs less frequently but it is also a symptom of this state of confusion. The redirect URL in Captive Portal may be set to go to the original URL which of course is the http://captive.apple.com...... which will display the "Success" text.
-
-
I had some time during lunch break, and went for it.
Step 1 :
Instead of your "dhcp_cp3.php" I've changed the file name to "rfc8910.php", and cleaned up a bit the file you proposed :<?php require_once("auth.inc"); require_once("util.inc"); require_once("functions.inc"); require_once("captiveportal.inc"); header("Expires: 0"); header("Cache-Control: no-cache, no-store, must-revalidate"); header("Pragma: no-cache"); header("Connection: close"); global $g, $config, $cpzone, $cpzoneid, $cpzoneprefix; $cpzone = strtolower($_REQUEST['zone']); $cpcfg = config_get_path("captiveportal/{$cpzone}"); if (empty($cpcfg)) { log_error("rfc8910 - Submission to captiveportal with unknown parameter zone: " . htmlspecialchars($cpzone)); portal_reply_page($redirurl, "error", gettext("Internal error")); ob_flush(); return; } $cpzoneid = $cpcfg['zoneid']; $clientip = $_SERVER['REMOTE_ADDR']; if (!$clientip) { /* not good - bail out */ log_error("Zone: {$cpzone} - rfc8910 - Captive portal could not determine client's IP address."); $errormsg = gettext("An error occurred. Please check the system logs for more information."); portal_reply_page($redirurl, "error", $errormsg); ob_flush(); return; } $cpsession = captiveportal_isip_logged($clientip); ob_flush(); if (empty($cpsession)) { header("Location: https://portal.bhf.tld:8003/rfc8910-T.json?zone={$cpzone}"); } else { header("Location: https://portal.bhf.tld:8003/rfc8910-F.json?zone={$cpzone}"); } ob_flush(); return; }
edit : a lot more stuff could be removed I guess. As small is beautiful .... but I was impatient, so I used "quick is fine also".
Step 2 :
I've created two more files in the /usr/local/captiveportal/ :[23.09.1-RELEASE][root@pfSense.bhf.tld]/usr/local/captiveportal: cat rfc8910-T.json { "captive": true, "user-portal-url": "https://portal.bhf.tld:8003/index.php?zone=cpzone1" } [23.09.1-RELEASE][root@pfSense.bhf.tld]/usr/local/captiveportal: cat rfc8910-F.json { "captive": false, "user-portal-url": "https://portal.bhf.tld:8003/index.php?zone=cpzone1" }
Step 3 :
Add option "114" to the captive portal network DHCP server - must must be ISC.
I've set custom option value to "https://portal.bhf.tld:8003/rfc8910.php?zone=cpzone1"
And yes, I have the pfsense captive portal web server running with a cert that says it's "portal.bhf.tld". It TLS port is 8003 in my case.
My captive portal zone is called "cpzone1"edit :
I did make a packet capture to inspect the DHCP handshake between captive portal pfSense DHCP server and the iPhone device, and saw that DHCP option 114 was there.Step 4 :
I deleted the Wifi/SSID "known network" from my iPhone.
And connected back to the captive portal.Worked flawlessly....... I guess.
I even have the impression it is faster, the login page shows up faster.So make sure I wasn't seeing things that I wanted to see, not that there ware actually not happening, I edited the the file and added a lot of log lines like this :
if (empty($cpsession)) {
captiveportal_logportalauth("rfc8910", "EMPTY SESSION", $clientip, $cpzone);
} else {
captiveportal_logportalauth("rfc8910", "EXISTING SESSION", $clientip, $cpzone);
}so I could see see what part was reached when under with conditions.
I actually saw that when connecting the iPhone to the captive portal SSID, it made a request for the URL, given to the phone with DHCP +option 114
Then the "rfc18910.php" file gave the phone the "rfc8910-T.json" file which indicated the phone that a portal exists.
So, according to the "rfc8910-T.json" content, it used the "https://portal.bhf.tld:8003/index.php?zone=cpzone1" which showed the login page on te phone..And all this magic worked after some plain copy paste (and some small edits).
Wow .... a RFC defined captive portal.
Thank you @EDaleHMinimal changes are needed - no pfSense scripts files changes are needed.
The bad news : works for iPhones only. Other phones : dono (probably not / later / I don't have an android device .... ).
-
@Gertjan
Excellent, nicely cleaned up.I got the impression it was working with Android but did not check it. We had fewer issues there too but I don't believe Android has the equivalent to "auto_logon=False".
I also think Windows is now recognizing 114 based on how fast it loads.In the midst of this, phone users have gotten better at understanding captive portals as well.
-
@EDaleH said in captive portal is not working on mobiles:
I also think Windows is now recognizing 114 based on how fast it loads.
I've brought my Windows 11 pro laptop in.
I removed from the known Wifi networks my captive portal record - and connected to portal network.
It looks like it totally ignores "DHCP option 114" as is didn't make any request for "rfc8910.php" -
Some progress :
/usr/local/captiveportal/rfc8910.php :
<?php require_once("auth.inc"); require_once("util.inc"); require_once("functions.inc"); require_once("captiveportal.inc"); header("Expires: 0"); header("Cache-Control: no-cache, no-store, must-revalidate"); header("Pragma: no-cache"); header("Connection: close"); global $g, $config, $cpzone, $cpzoneid, $cpzoneprefix; $cpzone = strtolower($_REQUEST['zone']); $cpcfg = config_get_path("captiveportal/{$cpzone}"); if (empty($cpcfg)) { log_error("rfc8910.php - Submission to captiveportal with unknown parameter zone: " . htmlspecialchars($cpzone)); portal_reply_page($redirurl, "error", gettext("Internal error")); ob_flush(); return; } $cpzoneid = $cpcfg['zoneid']; $clientip = $_SERVER['REMOTE_ADDR']; if (!$clientip) { /* not good - bail out */ log_error("Zone: {$cpzone} - rfc8910.php - Captive portal could not determine client's IP address."); $errormsg = gettext("An error occurred. Please check the system logs for more information."); portal_reply_page($redirurl, "error", $errormsg); ob_flush(); return; } $cpsession = captiveportal_isip_logged($clientip); $sessionid = $cpsession['sessionid']; ob_flush(); if (empty($cpsession)) { captiveportal_logportalauth("rfc8910", "EMPTY SESSION", $clientip, $cpzone); $seconds_remaining = $cpcfg['timeout']*60; $json_post = array ( 'captive' => true, 'user-portal-url' => "https://portal.bhf.tld:8003/index.php?zone=$cpzone", 'venue-info-url' => "https://portal.bhf.tld:8003/index.php?zone=$cpzone", 'seconds-remaining' => $seconds_remaining, 'can-extend-session' => true ); echo json_encode($json_post, JSON_PRETTY_PRINT); } else { captiveportal_logportalauth("rfc8910", "EXISTING SESSION", $clientip, $cpzone); $seconds_remaining = (time()-$cpsession['allow_time'])+($cpcfg['timeout']*60); $json_post = array ( 'captive' => false, 'user-portal-url' => "https://portal.bhf.tld:8003/index.php?zone=$cpzone", 'venue-info-url' => "https://portal.bhf.tld:8003/index.php?zone=$cpzone", 'seconds-remaining' => $seconds_remaining, 'can-extend-session' => true ); echo json_encode($json_post, JSON_PRETTY_PRINT); } ob_flush(); return; ?>
edit : for those who never look at logs, you could remove the two lines that start with "captiveportal_logportalauth( ....."
No more need to create/maintain/use the two json files "rfc8910-F.json" and "rfc8910-T.json", as they are now generated by rfc8910.php on the dynamically.
The
'venue-info-url' => "https://portal.bhf.tld:8003/index.php?zone=$cpzone",
will show useful info : tap on the SSID of the portal and something news shows up : and bring you to theURL you've chosen under 'venue-info-url'. I've put in place the same URL as the login page : what will happen is that, if the device is logged in already, the logout page is shown !
( Not that someone will make use of this - as you have to find it first )I'm also adding 'seconds-remaining', not sure the iPhone actually uses it.
Small recap about how to use all this :
Place the file I've shown above (this post) here : /usr/local/cativeportal/rfc8910.php
Then, make sure you use the ISC DHCP (no Kea for now), and add an "DHCP OPTION 114" :
My 'text string' : "https://portal.bhf.tld:8003/rfc8910.php?zone=cpzone1"
Where : "cpzone1" is the name of your captive portal zone.
"portal.bhf.tld" is the host name of your captive portal, which should TLS as it it mandatory.
Define the host name "portal.bhf.tld" as a host override under Services > DNS Resolver > General Settings.Take note : TLS means : you should won (rent) a domain name (otherwise the "captive portal" is not something for you).
And that's it.
From now on, devices that support RFC8910 will play nicely.
As no pfSense core files are edited, it's "upgrade" proof, and easy to put in place in case of a re install.
-
@Gertjan said in captive portal is not working on mobiles:
/usr/local/captiveportal/rfc8910.php
Integrating the json data into the code is cleaner but I would like to point out that every instance/zone of the captive portal needs a unique rfc8910.php file, thus I would name them like this: rfc8901_cp1.php, rfc8901_cp2.php,,, etc. If you were ambitious, you could likely create json data on the fly, changing sub-domain, port and zone info on the fly. The simplicity of separate files appeals to my KISS preferences.
@Gertjan said in captive portal is not working on mobiles:
captiveportal_logportalauth("rfc8910", "EXISTING SESSION", $clientip, $cpzone);
$seconds_remaining = (time()-$cpsession['allow_time'])+($cpcfg['timeout']*60);
$json_post = array (
'captive' => false,
'user-portal-url' => "https://portal.bhf.tld:8003/index.php?zone=$cpzone",
'venue-info-url' => "https://portal.bhf.tld:8003/index.php?zone=$cpzone",
'seconds-remaining' => $seconds_remaining,
'can-extend-session' => trueIncluding all the data supported is likely useful as devices adopt full functionality going forward. I do not use this because multiple connections to a single user does not currently accurately cumulate time or data and the result from the code can be simply wrong. Instead, we use custom code to calculate the time remaining for multiple connections to a single account. In that way we can apply a time/quota limit to the user and it will cumulate across all connections on that user account. As captive portal ends the session on a logout, we do not permit a logout, instead we publish a "dashboard" that shows time and data quota remaining or that user account. As stated above, although this launches (it is actually the logout code) in the temp browser, it immediately disappears when the temp browser closes. All of that only happens on the first connection to that Station ID/zone/Portal. For this reason, we provide the link to the "logout" code in our handout and instead it launches this dashboard of info when they want to check it. On Windows systems, there is no temp browser so the end user can leave this "logout" dashboard tab open and we refresh it's contents every 2.5 minutes.
-
@EDaleH said in captive portal is not working on mobiles:
but I would like to point out that every instance/zone of the captive portal needs a unique rfc8910.php file, thus I would name them like this: rfc8901_cp1.php, rfc8901_cp2.php,,, etc
Lol.
That's why I rewrote my rfc810Look again :
'user-portal-url' => "https://portal.bhf.tld:8003/index.php?zone=$cpzone",
the zone parameter is a variable, and the $cpzone available is extracted from the URL used to access the page (and to get the right portal session, etc).
I the device asks for a status update, the
$seconds_remaining = (time()-$cpsession['allow_time'])+($cpcfg['timeout']*60);
field is recalculated so the device knows that after xx seconds my hard time out will apply.I've just one portal instance, and will be testing with 'two' instances in a near future.
@EDaleH said in captive portal is not working on mobiles:
For this reason, we provide the link to the "logout" code
I've found it on the profile page on my iPhone device :
Clicking (taping) on Open (Ouvrir la page d'acceuil ...) will open the logout page - or whatever 'venue-info-url' points to.
As said earlier, no one will find it there.
Btw : I've added all the known RFC fields, just to see what happens - what is usefull, what is supported, etc.