$generator The config generator to return
*
- * @param string $generator The config generator class to return
- * @param User $user
- * @param ?string $passphrase Passphrase to encrypt the profile with
- * @param ?DateInterval $validity Period the profile will be valid for from generation
+ * @param string $generator The config generator class to return
+ * @param User $user
*
* @psalm-return T
*/
- public function getConfigGenerator( string $generator, User $user, ?string $passphrase = null, ?DateInterval $validity = null ): Generator
+ public function getConfigGenerator( string $generator, User $user, ?DateInterval $validity = null ): Generator
{
if ( null === $validity ) {
$validity = $this->manager->getDefaultValidity( $this->name );
@@ -68,7 +66,7 @@ public function getConfigGenerator( string $generator, User $user, ?string $pass
// TODO more generic method to get an arbitrary generator
$pkcs12 = $this->generateClientCertificate( $user, $expiry );
- return new $generator( $this->getProfileData(), [$this->createAuthenticationMethod( $pkcs12 )], $passphrase );
+ return new $generator( $this->getProfileData(), [$this->createAuthenticationMethod( $pkcs12 )] );
}
/**
@@ -96,14 +94,11 @@ public function getTrustedCaCertificates(): array
/**
* @param User $requester User requesting the certificate
- * @param string $commonName Common name of the server certificate, must be a hostname
+ * @param string $commonName Common name of the server certificate
* @param DateTimeInterface $expiry Expiry date
*/
public function generateServerCertificate( User $requester, string $commonName, DateTimeInterface $expiry ): PKCS12
{
- if ( !\filter_var( $commonName, \FILTER_VALIDATE_DOMAIN | \FILTER_NULL_ON_FAILURE ) ) {
- throw new InvalidArgumentException( 'Common name for a server certificate must be a hostname' );
- }
$serverKey = new PrivateKey( new OpenSSLConfig( OpenSSLConfig::KEY_EC ) );
$dn = new DN( ['CN' => $commonName] );
$csr = CSR::generate( $dn, $serverKey );
@@ -111,7 +106,7 @@ public function generateServerCertificate( User $requester, string $commonName,
$serial = $this->logPreparedServerCredential( $caCert, $requester, $csr, $expiry );
$caKey = $this->getSigningCAKey();
- $conf = new OpenSSLConfig( OpenSSLConfig::X509_SERVER + ['san' => 'DNS:' . $commonName] );
+ $conf = new OpenSSLConfig( OpenSSLConfig::X509_SERVER );
$serverCert = $csr->sign( $caCert, $caKey, $expiry, $conf, $serial );
$this->logCompletedServerCredential( $requester, $serverCert );
diff --git a/tpl/app.html b/tpl/app.html
index 799738a..5f05789 100644
--- a/tpl/app.html
+++ b/tpl/app.html
@@ -11,15 +11,12 @@ geteduroam– eduroam authentication made easy
{{ app.name }}
{% endfor %}
-{% if os_config %}
-
- For some platforms there are no apps available. For these platforms, you can download a configuration profile that you can install directly using the operating systems facilities.
+
+ For macOS, the current option is to install a .mobileconfig profile.
-{% endif %}
+
Options for other platforms and professional users
diff --git a/tpl/mobileconfig-mac-new.html b/tpl/mobileconfig-mac-new.html
new file mode 100644
index 0000000..901e687
--- /dev/null
+++ b/tpl/mobileconfig-mac-new.html
@@ -0,0 +1,21 @@
+{% extends "dialog.html" %}
+
+{% block title %}Apple mobileconfig{% endblock %}
+
+{% block content %}
+
+geteduroam– eduroam authentication made easy
+
+If you have macOS Big Sur or newer, please go to Profiles in System Preferences to complete installation
+
+
+{# We use a CSP with script-src: none, so this won't work:
+
+#}
+{% endblock %}
diff --git a/tpl/profile-advanced.html b/tpl/profile-advanced.html
deleted file mode 100644
index 3100a54..0000000
--- a/tpl/profile-advanced.html
+++ /dev/null
@@ -1,37 +0,0 @@
-{% extends "menu.html" %}
-
-{% block title %}New configuration profile{% endblock %}
-
-{% block content %}
-
- geteduroam– eduroam authentication made easy
- For most users, the easiest way to use geteduroam is to use one of the official apps.
-
-
-
-
-{% endblock %}
diff --git a/tpl/profile-download.html b/tpl/profile-download.html
deleted file mode 100644
index 66560d8..0000000
--- a/tpl/profile-download.html
+++ /dev/null
@@ -1,39 +0,0 @@
-{% extends "dialog.html" %}
-
-{% block title %}Apple mobileconfig{% endblock %}
-
-{% block content %}
-
-geteduroam– eduroam authentication made easy
-
-
-
-
-{% if passphrase %}
-When prompted for a passphrase during installation, enter the following passphrase:
-{{ passphrase }}
-
-{% endif %}
-
-{% if device == 'apple-mobileconfig' %}
-If you have macOS Big Sur or newer, please go to Profiles in System Preferences to complete installation.
-{% endif %}
-
-{% if device == 'google-onc' %}
-After downloading the file, open the Chrome browser and browse to this URL: chrome://network. Then, use the Import ONC file button. The import is silent; the new network definitions will be added to the preferred networks.
-{% endif %}
-
-
-
-{# We use a CSP with script-src: none, so this won't work:
-
-#}
-{% endblock %}
diff --git a/tpl/profiles-new.html b/tpl/profiles-new.html
new file mode 100644
index 0000000..b269f23
--- /dev/null
+++ b/tpl/profiles-new.html
@@ -0,0 +1,23 @@
+{% extends "menu.html" %}
+
+{% block title %}New configuration profile{% endblock %}
+
+{% block content %}
+
+ geteduroam– eduroam authentication made easy
+ For most users, the easiest way to use geteduroam is to use one of the official apps.
+
+
+
+
+{% endblock %}
diff --git a/www/app/index.php b/www/app/index.php
index c28177e..87d0dc1 100644
--- a/www/app/index.php
+++ b/www/app/index.php
@@ -34,15 +34,8 @@
'name' => 'Huawei',
],
],
- 'os_config' => [
- 'mobileconfig' => [
- 'url' => "${basePath}/profiles/mac/",
- 'name' => 'macOS',
- ],
- 'onc' => [
- 'url' => "${basePath}/profiles/onc/",
- 'name' => 'ChromeOS',
- ],
+ 'mobileconfig' => [
+ 'url' => "${basePath}/profiles/mac/",
],
'manual' => [
'url' => "${basePath}/profiles/new/",
diff --git a/www/assets/geteduroam.css b/www/assets/geteduroam.css
index 4c0988c..e2e6290 100644
--- a/www/assets/geteduroam.css
+++ b/www/assets/geteduroam.css
@@ -133,7 +133,7 @@ hr {
}
#loginform *:focus {
- outline: none;
+ outline: none;
}
body{
@@ -190,6 +190,7 @@ main h1:first-child {
ul.apps {
padding: 0;
margin: 0 auto;
+ max-width: 26em;
}
ul.apps li a.btn {
min-width: 6em;
@@ -201,12 +202,6 @@ ul.buttons li {
margin-bottom: .5em;
}
-#profile-encrypt {
- margin-bottom: 2em;
-}
-#profile-encrypt input, #passphrase-show {
- font-size: 2em;
-}
@media (prefers-color-scheme: dark) {
@@ -287,8 +282,8 @@ ul.buttons li {
}
.apps {
- text-align: center;
- text-align-last: center;
+ text-align: justify;
+ text-align-last: justify;
}
ul.apps li {
display: inline;
diff --git a/www/index.php b/www/index.php
index 83b7ac4..af1b97e 100644
--- a/www/index.php
+++ b/www/index.php
@@ -26,5 +26,5 @@
$app->render( [
'href' => "${basePath}/",
- 'http://letswifi.app/api#v2' => $apiConfiguration,
+ 'http://letswifi.app/api#v1' => $apiConfiguration,
], 'info', $basePath );
diff --git a/www/profiles/mac/index.php b/www/profiles/mac/index.php
index 4c5cba1..23ea9db 100644
--- a/www/profiles/mac/index.php
+++ b/www/profiles/mac/index.php
@@ -12,7 +12,34 @@
$basePath = '../..';
\assert( \array_key_exists( 'REQUEST_METHOD', $_SERVER ) );
-$downloadKind = 'apple-mobileconfig';
-$href = "${basePath}/profiles/mac/";
+$app = new letswifi\LetsWifiApp();
+$app->registerExceptionHandler();
+$realm = $app->getRealm();
+// just trigger a login
+$app->getUserFromBrowserSession( $realm );
-require \implode(\DIRECTORY_SEPARATOR, [\dirname(__DIR__), 'new', '_download.php']);
+// Create a short-lived cookie to allow the user ONE download without using POST
+// If the download would fail, the user is still presented with a download button
+// on this page, which uses a more reliable POST.
+// If the meta_redirect would go through too late (after cookie expiry),
+// the page being redirected to will also contain an appropriate download button.
+\setcookie('mobileconfig-download-token', (string)\time(), [
+ 'expires' => 0, // session cookie
+ 'httponly' => true, // not available in JavaScript
+ 'secure' => false, // we don't care, this is not for security, and this helps with local devving
+ 'path' => '/', // make it available to /profiles/new as well; relative path's don't work here so use "/" for now
+ 'samesite' => 'Strict',
+]);
+
+switch ( $_SERVER['REQUEST_METHOD'] ) {
+ case 'GET': return $app->render(
+ [
+ 'href' => "${basePath}/profiles/mac/",
+ 'action' => "${basePath}/profiles/new/",
+ 'device' => 'apple-mobileconfig',
+ 'meta_redirect' => "${basePath}/profiles/new/?" . \http_build_query( ['download' => '1', 'device' => 'apple-mobileconfig'] ),
+ ], 'mobileconfig-mac-new', $basePath, );
+}
+
+\header( 'Content-Type: text/plain', true, 405 );
+exit( "405 Method Not Allowed\r\n" );
diff --git a/www/profiles/new/_download.php b/www/profiles/new/_download.php
deleted file mode 100644
index e0d8d0a..0000000
--- a/www/profiles/new/_download.php
+++ /dev/null
@@ -1,57 +0,0 @@
-
- * Copyright: 2020-2022, Paul Dekkers, SURF
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-if ( !isset( $downloadKind ) || !isset( $href ) || !isset( $basePath ) ) {
- \header( 'Content-Type: text/plain', true, 400 );
- exit( "400 Bad Request\r\n\r\nInvalid request\r\n" );
-}
-\assert( \array_key_exists( 'REQUEST_METHOD', $_SERVER ) );
-
-$app = new letswifi\LetsWifiApp();
-$app->registerExceptionHandler();
-$realm = $app->getRealm();
-// just trigger a login
-$app->getUserFromBrowserSession( $realm );
-
-// Create a short-lived cookie to allow the user ONE download without using POST
-// If the download would fail, the user is still presented with a download button
-// on this page, which uses a more reliable POST.
-// If the meta_redirect would go through too late (after cookie expiry),
-// the page being redirected to will also contain an appropriate download button.
-\setcookie( "${downloadKind}-download-token", (string)\time(), [
- 'expires' => 0, // session cookie
- 'httponly' => true, // not available in JavaScript
- 'secure' => false, // we don't care, this is not for security, and this helps with local devving
- 'path' => '/', // make it available to /profiles/new as well; relative path's don't work here so use "/" for now
- 'samesite' => 'Strict',
-] );
-if ( isset( $passphrase ) ) {
- \setcookie( "${downloadKind}-download-passphrase", $passphrase, [
- 'expires' => 0, // session cookie
- 'httponly' => true, // not available in JavaScript
- 'secure' => false, // we don't care, this is not for security, and this helps with local devving
- 'path' => '/', // make it available to /profiles/new as well; relative path's don't work here so use "/" for now
- 'samesite' => 'Strict',
- ] );
-}
-
-switch ( $_SERVER['REQUEST_METHOD'] ) {
- case 'GET': return $app->render(
- [
- 'href' => $href,
- 'passphrase' => ( $passphrase ?? null ) ?: null,
- 'action' => "${basePath}/profiles/new/",
- 'device' => $downloadKind,
- 'meta_redirect' => "${basePath}/profiles/new/?" . \http_build_query( ['download' => '1', 'device' => $downloadKind] ),
- ], 'profile-download', $basePath, );
-}
-
-\header( 'Content-Type: text/plain', true, 405 );
-exit( "405 Method Not Allowed\r\n" );
diff --git a/www/profiles/new/index.php b/www/profiles/new/index.php
index 22066d8..a9fcffb 100644
--- a/www/profiles/new/index.php
+++ b/www/profiles/new/index.php
@@ -17,7 +17,7 @@
$realm = $app->getRealm();
$user = $app->getUserFromBrowserSession( $realm );
-// Workaround for MacOS/ChromeOS flow; we want to provide the download through a meta refresh,
+// Workaround for MacOS flow; we want to provide the download through a meta refresh,
// but the refresh should only work once.
// Here we check a session, check if it can provide a download and then destroy the cookie
// so that the next request would point the user to the download page instead of the
@@ -40,40 +40,29 @@
// It also does not protect against scripted downloads;
// the cookie is easily guessable, but why would a script do that?
// It can just POST.
+if ( isset( $_COOKIE['mobileconfig-download-token'] ) ) {
+ \setcookie( 'mobileconfig-download-token', 'delete', 100000 );
+}
if ( 'GET' === $_SERVER['REQUEST_METHOD'] && isset( $_GET['download'] ) ) {
- foreach ( ['apple-mobileconfig', 'google-onc', 'pkcs12'] as $kind ) {
- // Ensure this request can only be served one time
- // Does not affect the current $_COOKIE variable
- \setcookie("${kind}-download-token", '', [
- 'expires' => 0,
- 'httponly' => true,
- 'secure' => false,
- 'path' => '/',
- 'samesite' => 'Strict',
- ]);
-
- if ( $_GET['device'] === $kind && isset( $_COOKIE["${kind}-download-token"] ) ) {
- $cookieTime = (int)$_COOKIE["${kind}-download-token"];
- $earliestOkTime = \time() - 60; // allow cookies up to 60 seconds old
- if ( \time() >= $cookieTime && $cookieTime >= $earliestOkTime ) {
- $overrideMethod = 'POST';
- $overrideDevice = $kind;
- }
- if ( isset( $_COOKIE["${kind}-download-passphrase"] ) ) {
- $overridePassphrase = $_COOKIE["${kind}-download-passphrase"] ?: null;
- }
+ if ( isset( $_COOKIE['mobileconfig-download-token'] ) ) {
+ $cookieTime = (int)$_COOKIE['mobileconfig-download-token'];
+ $earliestOkTime = \time() - 60; // allow cookies up to 60 seconds old
+ if ( \time() >= $cookieTime && $cookieTime >= $earliestOkTime ) {
+ $fakeMethod = 'POST';
+ //$fakeDevice = (string)$_GET['device'];
+ $fakeDevice = 'apple-mobileconfig';
}
}
// If we're not willing to fake a POST,
// also remove the GET parameters that attempted this from the URL
- if ( !isset( $overrideMethod ) && \array_key_exists( 'REQUEST_URI', $_SERVER ) ) {
+ if ( !isset( $fakeMethod ) && \array_key_exists( 'REQUEST_URI', $_SERVER ) ) {
\header( 'Location: ' . \strstr( $_SERVER['REQUEST_URI'], '?', true ) );
exit;
}
}
-switch ( $overrideMethod ?? $_SERVER['REQUEST_METHOD'] ) {
+switch ( $fakeMethod ?? $_SERVER['REQUEST_METHOD'] ) {
case 'GET': return $app->render(
[
'href' => "${basePath}/profiles/new/",
@@ -84,9 +73,6 @@
'eap-config' => [
'name' => 'eap-config',
],
- 'google-onc' => [
- 'name' => 'ChromeOS',
- ],
'pkcs12' => [
'name' => 'PKCS12',
],
@@ -94,19 +80,12 @@
'app' => [
'url' => "${basePath}/app/",
],
- ], 'profile-advanced', $basePath, );
+ ], 'profiles-new', $basePath, );
case 'POST':
- $passphrase = $overridePassphrase ?? $_POST['passphrase'] ?: null;
- if ( \is_array( $passphrase ) ) {
- \header( 'Content-Type: text/plain', true, 400 );
- exit( "400 Bad Request\r\n\r\nInvalid passphrase\r\n" );
- }
- switch ( $device = $overrideDevice ?? $_POST['device'] ?? '' ) {
- case 'apple-mobileconfig': $generator = $realm->getConfigGenerator( \letswifi\profile\generator\MobileConfigGenerator::class, $user, $passphrase ); break;
- case 'eap-config': $generator = $realm->getConfigGenerator( \letswifi\profile\generator\EapConfigGenerator::class, $user, $passphrase ); break;
- case 'pkcs12': $generator = $realm->getConfigGenerator( \letswifi\profile\generator\PKCS12Generator::class, $user, $passphrase ); break;
- case 'google-onc': $generator = $realm->getConfigGenerator( \letswifi\profile\generator\ONCGenerator::class, $user, $passphrase ); break;
-
+ switch ( $device = $fakeDevice ?? $_POST['device'] ?? '' ) {
+ case 'apple-mobileconfig': $generator = $realm->getConfigGenerator( \letswifi\profile\generator\MobileConfigGenerator::class, $user ); break;
+ case 'eap-config': $generator = $realm->getConfigGenerator( \letswifi\profile\generator\EapConfigGenerator::class, $user ); break;
+ case 'pkcs12': $generator = $realm->getConfigGenerator( \letswifi\profile\generator\PKCS12Generator::class, $user ); break;
default:
\header( 'Content-Type: text/plain', true, 400 );
$deviceStr = \is_string( $device )
diff --git a/www/profiles/onc/index.php b/www/profiles/onc/index.php
deleted file mode 100644
index 006fa2d..0000000
--- a/www/profiles/onc/index.php
+++ /dev/null
@@ -1,19 +0,0 @@
-
- * Copyright: 2020-2022, Paul Dekkers, SURF
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-require \implode(\DIRECTORY_SEPARATOR, [\dirname(__DIR__, 3), 'src', '_autoload.php']);
-$basePath = '../..';
-\assert( \array_key_exists( 'REQUEST_METHOD', $_SERVER ) );
-
-$downloadKind = 'google-onc';
-$href = "${basePath}/profiles/onc/";
-$passphrase = $_COOKIE["${downloadKind}-download-passphrase"] ?: \substr( '000' . \random_int( 0, 9999 ), -4 );
-
-require \implode(\DIRECTORY_SEPARATOR, [\dirname(__DIR__), 'new', '_download.php']);