Skip to content

Commit 98bab46

Browse files
committed
Add files
0 parents  commit 98bab46

24 files changed

+4336
-0
lines changed

api/.htaccess

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
RewriteEngine On
2+
3+
RewriteRule ^.*$ routing.php [L]

api/components/Jwt.php

Lines changed: 637 additions & 0 deletions
Large diffs are not rendered by default.

api/components/Key.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
namespace Firebase\JWT;
4+
5+
use InvalidArgumentException;
6+
use OpenSSLAsymmetricKey;
7+
use OpenSSLCertificate;
8+
use TypeError;
9+
10+
class Key
11+
{
12+
/** @var string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate */
13+
private $keyMaterial;
14+
/** @var string */
15+
private $algorithm;
16+
17+
/**
18+
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial
19+
* @param string $algorithm
20+
*/
21+
public function __construct(
22+
$keyMaterial,
23+
string $algorithm
24+
) {
25+
if (
26+
!\is_string($keyMaterial)
27+
&& !$keyMaterial instanceof OpenSSLAsymmetricKey
28+
&& !$keyMaterial instanceof OpenSSLCertificate
29+
&& !\is_resource($keyMaterial)
30+
) {
31+
throw new TypeError('Key material must be a string, resource, or OpenSSLAsymmetricKey');
32+
}
33+
34+
if (empty($keyMaterial)) {
35+
throw new InvalidArgumentException('Key material must not be empty');
36+
}
37+
38+
if (empty($algorithm)) {
39+
throw new InvalidArgumentException('Algorithm must not be empty');
40+
}
41+
42+
// TODO: Remove in PHP 8.0 in favor of class constructor property promotion
43+
$this->keyMaterial = $keyMaterial;
44+
$this->algorithm = $algorithm;
45+
}
46+
47+
/**
48+
* Return the algorithm valid for this key
49+
*
50+
* @return string
51+
*/
52+
public function getAlgorithm(): string
53+
{
54+
return $this->algorithm;
55+
}
56+
57+
/**
58+
* @return string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate
59+
*/
60+
public function getKeyMaterial()
61+
{
62+
return $this->keyMaterial;
63+
}
64+
}

api/main.php

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
<?php
2+
require_once 'components/Jwt.php';
3+
require_once 'components/Key.php';
4+
5+
class Session {
6+
public function __construct(
7+
public string $sessionName,
8+
) {}
9+
10+
public function get() {
11+
if(!isset($_COOKIE[$this->sessionName]))
12+
throw new Exception("Access token cookie not set");
13+
14+
try {
15+
$session = Firebase\JWT\JWT::decode($_COOKIE[$this->sessionName], new Firebase\JWT\Key('yWvati2Z94ZV6XFaSwC7gqdsabtTYHqMnzWB7o58AvCXHkheS8ANfHLKTTwXE9cXHg7GH2bSb9q95Lo2He9XDWtqDBwJzvKFbX8ymeLPkhFQkJxF8GDbmpRUfeXctiLi', 'HS256'));
16+
}
17+
catch (Exception) {
18+
setcookie('accessToken', '', 0, '/'); // delete cookie on browser
19+
throw new Exception("Failed to decode access token");
20+
}
21+
22+
return new Account(id: (int) $session->id , auth: (bool) $session->auth, mfa: $session->mfa, email: $session->email);
23+
}
24+
public function set($sessionValue) {
25+
setcookie($this->sessionName, $sessionValue, time()+86400, "/");
26+
return;
27+
}
28+
public function start($payload) {
29+
$encoded = Firebase\JWT\JWT::encode($payload, 'yWvati2Z94ZV6XFaSwC7gqdsabtTYHqMnzWB7o58AvCXHkheS8ANfHLKTTwXE9cXHg7GH2bSb9q95Lo2He9XDWtqDBwJzvKFbX8ymeLPkhFQkJxF8GDbmpRUfeXctiLi', 'HS256');
30+
$this->set($encoded);
31+
}
32+
}
33+
34+
class Account {
35+
public function __construct(
36+
public ?int $id = null,
37+
public ?bool $auth = null,
38+
public ?string $mfa = null,
39+
public ?string $email = null,
40+
) {}
41+
42+
public function isAuthenticated() {
43+
return $this->auth;
44+
}
45+
46+
public function register($password) {
47+
if(empty($this->email))
48+
throw new Exception("Email has not been specified");
49+
50+
if(empty($password))
51+
throw new Exception("Password has not been specified");
52+
53+
$db = new Database("localhost", "root", "", "jwt-webauthn-php");
54+
$query = $db->query("SELECT 1 FROM `accounts` WHERE `email` = ?", [$this->email]);
55+
if ($query->num_rows >= 1) {
56+
throw new Exception("Email already taken!");
57+
}
58+
59+
$passHashed = password_hash($password, PASSWORD_BCRYPT);
60+
61+
$query = $db->query("INSERT INTO `accounts` (`email`, `password`) VALUES (?, ?)", [$this->email, $passHashed]);
62+
if ($query->affected_rows > 0) {
63+
$session = new Session("accessToken");
64+
$session->start(array("id" => (int) $query->insert_id, "auth" => true, "mfa" => null, "email" => $this->email));
65+
return true;
66+
}
67+
else {
68+
throw new Exception("Failed to register account");
69+
}
70+
}
71+
72+
public function login($password) {
73+
if(empty($this->email))
74+
throw new Exception("Email has not been specified");
75+
76+
if(empty($password))
77+
throw new Exception("Password has not been specified");
78+
79+
$db = new Database("localhost", "root", "", "jwt-webauthn-php");
80+
$query = $db->query("SELECT * FROM `accounts` WHERE `email` = ?", [$this->email]);
81+
if ($query->num_rows < 1) {
82+
throw new Exception("Email not found!");
83+
}
84+
85+
while ($row = mysqli_fetch_array($query->result)) {
86+
$id = $row['id'];
87+
$passHashed = $row['password'];
88+
$securityKey = $row['securityKey'];
89+
}
90+
91+
if (password_verify($password, $passHashed)) {
92+
$session = new Session("accessToken");
93+
$session->start(array("id" => (int) $id, "auth" => !$securityKey, "mfa" => $securityKey ? "webauthn" : null, "email" => $this->email));
94+
}
95+
else {
96+
throw new Exception("Password is invalid");
97+
}
98+
99+
if($securityKey)
100+
throw new Exception("Security key required.");
101+
102+
return true;
103+
}
104+
}
105+
106+
class Database {
107+
private $connection;
108+
109+
public function __construct($databaseHost, $databaseUsername, $databasePassword, $databaseName) {
110+
$this->connection = new mysqli($databaseHost, $databaseUsername, $databasePassword, $databaseName);
111+
112+
if(!$this->connection)
113+
throw new Exception($this->connection->connect_error);
114+
}
115+
116+
public function __destruct() {
117+
$this->connection->close();
118+
}
119+
120+
public function query($query, $args = [], $types = null) {
121+
if (is_null($types) && !empty($args))
122+
$types = str_repeat('s', count($args)); // unless otherwise specified, set type to string
123+
124+
$stmt = $this->connection->prepare($query);
125+
126+
if (!$stmt)
127+
throw new Exception($this->connection->error);
128+
129+
if (str_contains($query, "?"))
130+
$stmt->bind_param($types, ...$args);
131+
132+
$stmt->execute();
133+
134+
$query = new \stdClass();
135+
$query->result = $stmt->get_result();
136+
$query->num_rows = $query->result->num_rows;
137+
$query->affected_rows = $stmt->affected_rows;
138+
$query->insert_id = $stmt->insert_id;
139+
140+
$stmt->close();
141+
142+
return $query;
143+
}
144+
}
145+
146+
class Response {
147+
public function __construct(
148+
public ?int $status = null,
149+
public ?array $data = null,
150+
public ?string $error = null,
151+
public ?string $message = null,
152+
public ?string $env = null,
153+
public ?string $log = null,
154+
) {}
155+
156+
public function send() {
157+
header("Content-type: application/json");
158+
die(json_encode(array("status" => (int) $this->status, "data" => $this->data, "error" => $this->error, "message" => $this->message, "env" => $this->env, "log" => $this->log)));
159+
}
160+
}

0 commit comments

Comments
 (0)