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