Skip to content

Commit 1f0dbe0

Browse files
NFC-102 Web eID for Mobile authentication support for web-eid
Signed-off-by: Sander Kondratjev <sander.kondratjev@nortal.com>
1 parent 891c6a2 commit 1f0dbe0

19 files changed

Lines changed: 199 additions & 141 deletions

example/src/Auth.php

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,27 @@
11
<?php
22

3-
use web_eid\web_eid_authtoken_validation_php\authtoken\WebEidAuthToken;
3+
/*
4+
* Copyright (c) 2025-2025 Estonian Information System Authority
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
425
use web_eid\web_eid_authtoken_validation_php\challenge\ChallengeNonceStore;
526
use web_eid\web_eid_authtoken_validation_php\exceptions\AuthTokenParseException;
627
use web_eid\web_eid_authtoken_validation_php\exceptions\ChallengeNonceExpiredException;
@@ -43,6 +64,7 @@ public function getNonce()
4364
public function validate()
4465
{
4566
$this->ctx->assertCsrf();
67+
$this->ctx->assertJsonContentType();
4668
$authToken = file_get_contents("php://input");
4769

4870
try {
@@ -55,17 +77,22 @@ public function validate()
5577
$challengeNonce->getBase64EncodedNonce()
5678
);
5779

80+
session_regenerate_id();
81+
5882
echo json_encode([
5983
"sub" => $subjectName
6084
]);
6185
} catch (ChallengeNonceExpiredException) {
62-
http_response_code(400);
86+
unset($_SESSION["auth-user"]);
87+
http_response_code(401);
6388
echo "Challenge nonce not found or expired";
6489
} catch (ChallengeNonceNotFoundException) {
65-
http_response_code(400);
90+
unset($_SESSION["auth-user"]);
91+
http_response_code(401);
6692
echo "Challenge nonce not found";
6793
} catch (AuthTokenParseException) {
68-
http_response_code(400);
94+
unset($_SESSION["auth-user"]);
95+
http_response_code(401);
6996
echo "Invalid authentication token";
7097
}
7198
}

example/src/AuthContext.php

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
11
<?php
22

3+
/*
4+
* Copyright (c) 2025-2025 Estonian Information System Authority
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
325
use web_eid\web_eid_authtoken_validation_php\authtoken\WebEidAuthToken;
426
use web_eid\web_eid_authtoken_validation_php\certificate\CertificateData;
527
use web_eid\web_eid_authtoken_validation_php\certificate\CertificateLoader;
@@ -27,6 +49,11 @@ public function mobileBaseUrl(): string
2749
return rtrim($this->config->get('mobile_base_url'), '/');
2850
}
2951

52+
public function mobileRequestSigningCert(): bool
53+
{
54+
return (bool) $this->config->get('mobile_request_signing_cert');
55+
}
56+
3057
/**
3158
* @throws AuthTokenParseException
3259
*/
@@ -68,6 +95,25 @@ public function assertCsrf(bool $jsonError = true): void
6895
}
6996
}
7097

98+
public function assertJsonContentType(bool $jsonError = true): void
99+
{
100+
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
101+
102+
if (!str_starts_with(strtolower($contentType), 'application/json')) {
103+
http_response_code(415);
104+
105+
if ($jsonError) {
106+
echo json_encode([
107+
"error" => "Content-Type must be application/json"
108+
]);
109+
} else {
110+
echo "Invalid Content-Type, expected application/json";
111+
}
112+
exit;
113+
}
114+
}
115+
116+
71117
public function trustedIntermediateCACertificates(): array
72118
{
73119
$directory = __DIR__ . "/../certificates/";
@@ -89,7 +135,7 @@ public function tokenValidator(): AuthTokenValidator
89135
return (new AuthTokenValidatorBuilder($logger))
90136
->withSiteOrigin(new Uri($this->config->get('origin_url')))
91137
->withTrustedCertificateAuthorities(...$this->trustedIntermediateCACertificates())
92-
->withoutUserCertificateRevocationCheckWithOcsp() // TODO TEST CARDS
138+
->withOcspRequestTimeout(5)
93139
->build();
94140
}
95141

example/src/Config.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public static function fromArray($configArr)
3636
public function overrideFromEnv()
3737
{
3838
foreach ($this->configArr as $key => $value) {
39-
$envKey = 'WEB_EID_SAMPLE_'.strtoupper($key);
39+
$envKey = 'WEB_EID_SAMPLE_ORIGIN_URL'.strtoupper($key);
4040
$envValue = getenv($envKey);
4141
if ($envValue !== false) {
4242
$this->configArr[$key] = $envValue;
@@ -50,4 +50,4 @@ public function get($name)
5050
{
5151
return isset ($this->configArr[$name]) ? $this->configArr[$name] : null;
5252
}
53-
}
53+
}

example/src/MobileAuth.php

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,27 @@
11
<?php
22

3-
use web_eid\web_eid_authtoken_validation_php\authtoken\WebEidAuthToken;
3+
/*
4+
* Copyright (c) 2025-2025 Estonian Information System Authority
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
425
use web_eid\web_eid_authtoken_validation_php\challenge\ChallengeNonceStore;
526

627
final class MobileAuth
@@ -20,20 +41,25 @@ public function init(): void
2041
$payload = [
2142
"challenge" => $challenge->getBase64EncodedNonce(),
2243
"login_uri" => $this->ctx->originUrl() . "/auth/mobile/login",
23-
"get_signing_certificate" => true
44+
"get_signing_certificate" => $this->ctx->mobileRequestSigningCert()
2445
];
2546

26-
$authUri =
27-
$this->ctx->mobileBaseUrl() .
28-
"//auth#" .
29-
base64_encode(json_encode($payload));
47+
$baseUrl = $this->ctx->mobileBaseUrl();
48+
$encodedPayload = base64_encode(json_encode($payload));
49+
50+
if (str_starts_with($baseUrl, 'http')) {
51+
$authUri = rtrim($baseUrl, '/') . '/auth#' . $encodedPayload;
52+
} else {
53+
$authUri = rtrim($baseUrl, '/') . '//auth#' . $encodedPayload;
54+
}
3055

3156
echo json_encode(["auth_uri" => $authUri]);
3257
}
3358

3459
public function login(): void
3560
{
3661
$this->ctx->assertCsrf();
62+
$this->ctx->assertJsonContentType();
3763

3864
$json = json_decode(file_get_contents("php://input"), true);
3965
if (!isset($json["auth_token"])) {
@@ -50,10 +76,16 @@ public function login(): void
5076
$nonce->getBase64EncodedNonce()
5177
);
5278

79+
session_regenerate_id();
80+
5381
echo json_encode(["redirect" => "/welcome"]);
54-
} catch (Exception $e) {
55-
http_response_code(400);
56-
echo json_encode(["error" => "Validation failed"]);
82+
} catch (Throwable $e) {
83+
error_log("Authentication failed: " . $e->getMessage());
84+
85+
unset($_SESSION["auth-user"]);
86+
87+
http_response_code(401);
88+
echo json_encode(["error" => "Authentication failed"]);
5789
}
5890
}
5991
}

example/src/app.conf.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22

33
return [
4-
'origin_url' => 'https://157937465d58.ngrok-free.app',
4+
'origin_url' => 'https://localhost',
55
'mobile_base_url' => 'web-eid-mobile://',
66
'mobile_request_signing_cert' => false
7-
];
7+
];

src/authtoken/SupportedSignatureAlgorithm.php

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
* SOFTWARE.
2323
*/
2424

25+
declare(strict_types=1);
26+
2527
namespace web_eid\web_eid_authtoken_validation_php\authtoken;
2628

2729
class SupportedSignatureAlgorithm
@@ -45,31 +47,16 @@ public function getCryptoAlgorithm(): string
4547
return $this->cryptoAlgorithm;
4648
}
4749

48-
public function setCryptoAlgorithm(string $cryptoAlgorithm): void
49-
{
50-
$this->cryptoAlgorithm = $cryptoAlgorithm;
51-
}
52-
5350
public function getHashFunction(): string
5451
{
5552
return $this->hashFunction;
5653
}
5754

58-
public function setHashFunction(string $hashFunction): void
59-
{
60-
$this->hashFunction = $hashFunction;
61-
}
62-
6355
public function getPaddingScheme(): string
6456
{
6557
return $this->paddingScheme;
6658
}
6759

68-
public function setPaddingScheme(string $paddingScheme): void
69-
{
70-
$this->paddingScheme = $paddingScheme;
71-
}
72-
7360
public static function fromArray(array $data): self
7461
{
7562
return new self(
@@ -87,4 +74,4 @@ public function toArray(): array
8774
'paddingScheme' => $this->paddingScheme,
8875
];
8976
}
90-
}
77+
}

src/authtoken/WebEidAuthToken.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class WebEidAuthToken
4949
*/
5050
private ?string $format = null;
5151
/**
52-
* @var string Unverfied signing certificate
52+
* @var string Unverified signing certificate
5353
*/
5454
private ?string $unverifiedSigningCertificate = null;
5555

src/certificate/CertificateLoader.php

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@
2626

2727
namespace web_eid\web_eid_authtoken_validation_php\certificate;
2828

29-
use SodiumException;
3029
use web_eid\web_eid_authtoken_validation_php\exceptions\CertificateDecodingException;
30+
use web_eid\web_eid_authtoken_validation_php\exceptions\AuthTokenParseException;
3131
use phpseclib3\File\X509;
3232
use BadFunctionCallException;
33+
use Throwable;
3334

3435
final class CertificateLoader
3536
{
@@ -61,27 +62,18 @@ public static function loadCertificatesFromResources(string ...$resourceNames):
6162
return $caCertificates;
6263
}
6364

64-
/**
65-
* @throws CertificateDecodingException
66-
*/
67-
public static function decodeCertificateFromBase64(string $base64): X509
65+
public static function decodeCertificateFromBase64(?string $base64, string $fieldName = 'certificate'): X509
6866
{
69-
$cert = new X509();
70-
71-
if (!str_starts_with($base64, '-----BEGIN CERTIFICATE-----')) {
72-
$base64 = "-----BEGIN CERTIFICATE-----\n" .
73-
chunk_split($base64, 64) .
74-
"-----END CERTIFICATE-----\n";
67+
if ($base64 === null || $base64 === '') {
68+
throw new AuthTokenParseException("'{$fieldName}' field is missing, null or empty");
7569
}
76-
70+
$cert = new X509();
7771
try {
7872
if (!$cert->loadX509($base64)) {
79-
throw new CertificateDecodingException(
80-
"unverifiedSigningCertificate is not base64 encoded");
73+
throw new CertificateDecodingException("'{$fieldName}' decode failed");
8174
}
82-
} catch (SodiumException) {
83-
throw new CertificateDecodingException(
84-
"unverifiedSigningCertificate is not base64 encoded");
75+
} catch (Throwable) {
76+
throw new CertificateDecodingException("'{$fieldName}' decode failed");
8577
}
8678

8779
return $cert;

0 commit comments

Comments
 (0)