Skip to content

Commit a1fba19

Browse files
committed
Add login endpoint with JWT token generated
1 parent a60881c commit a1fba19

File tree

10 files changed

+87
-7
lines changed

10 files changed

+87
-7
lines changed

.env.dist

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# API URL
2+
APP_URL="http://localhost:8080"
3+
4+
JWT_KEY=""
5+
JWT_ALGO_ENCRYPTION="HS512"
6+
17
# Database details
28
DB_HOST=""
39
DB_NAME=""

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# Environment file
12
.env
23

34
# Depedency files

src/Dal/UserDal.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,23 @@ public static function update(string $userUuid, UserEntity $userEntity): int|str
6767
return false;
6868
}
6969

70-
public static function get(string $userUuid): ?array
70+
public static function getById(string $userUuid): ?array
7171
{
7272
$bindings = ['userUuid' => $userUuid];
7373
$userBean = R::findOne(self::TABLE_NAME, 'user_uuid = :userUuid ', $bindings);
7474

7575
return $userBean?->export();
7676
}
7777

78+
public static function getByEmail(string $email): ?array
79+
{
80+
$bindings = ['email' => $email];
81+
82+
$userBean = R::findOne(self::TABLE_NAME, 'email = :email', $bindings);
83+
84+
return $userBean?->export();
85+
}
86+
7887
public static function getAll(): array
7988
{
8089
return R::findAll(self::TABLE_NAME);

src/Route/Exception/NotFoundException.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
namespace PH7\ApiSimpleMenu\Route\Exception;
44

5-
use Exception;
5+
use RuntimeException;
66

7-
class NotFoundException extends Exception
7+
class NotFoundException extends RuntimeException
88
{
99
}

src/Route/routes.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
namespace PH7\ApiSimpleMenu\Route;
33

44
use PH7\ApiSimpleMenu\Route\Exception\NotFoundException;
5+
use PH7\ApiSimpleMenu\Service\Exception\CredentialsInvalidException;
56

67
$resource = $_REQUEST['resource'] ?? null;
78

@@ -11,7 +12,11 @@
1112
'item' => require_once 'food-item.routes.php',
1213
default => require_once 'not-found.routes.php',
1314
};
15+
} catch (CredentialsInvalidException $e) {
16+
response([
17+
'message' => $e->getMessage()
18+
]);
1419
} catch (NotFoundException $e) {
1520
// FYI, not-found.Route already sends a 404 Not Found HTTP code
1621
return require_once 'not-found.routes.php';
17-
}
22+
}

src/Route/user.routes.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
enum UserAction: string
1313
{
14+
case LOGIN = 'login';
1415
case CREATE = 'create';
1516
case RETRIEVE_ALL = 'retrieveall';
1617
case RETRIEVE = 'retrieve';
@@ -32,6 +33,7 @@ public function getResponse(): string
3233
try {
3334
// check first if HTTP method for the requested endpoint is valid
3435
$expectHttpMethod = match ($this) {
36+
self::LOGIN => Http::POST_METHOD,
3537
self::CREATE => Http::POST_METHOD,
3638
self::UPDATE => Http::POST_METHOD,
3739
self::RETRIEVE_ALL => Http::GET_METHOD,
@@ -44,6 +46,7 @@ public function getResponse(): string
4446
}
4547

4648
$response = match ($this) {
49+
self::LOGIN => $user->login($postBody),
4750
self::CREATE => $user->create($postBody),
4851
self::UPDATE => $user->update($postBody),
4952
self::RETRIEVE_ALL => $user->retrieveAll(),
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace PH7\ApiSimpleMenu\Service\Exception;
4+
5+
use RuntimeException;
6+
7+
class CredentialsInvalidException extends RuntimeException
8+
{
9+
}

src/Service/User.php

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
<?php
22
namespace PH7\ApiSimpleMenu\Service;
33

4+
use Firebase\JWT\JWT;
45
use PH7\ApiSimpleMenu\Dal\UserDal;
56
use PH7\ApiSimpleMenu\Service\Exception\EmailExistsException;
7+
use PH7\ApiSimpleMenu\Service\Exception\CredentialsInvalidException;
68
use PH7\ApiSimpleMenu\Validation\Exception\InvalidValidationException;
79
use PH7\ApiSimpleMenu\Validation\UserValidation;
810
use PH7\JustHttp\StatusCode;
@@ -15,6 +17,42 @@ class User
1517
{
1618
public const DATE_TIME_FORMAT = 'Y-m-d H:i:s';
1719

20+
public function login(mixed $data)
21+
{
22+
$userValidation = new UserValidation($data);
23+
if ($userValidation->isLoginSchemaValid()) {
24+
if (UserDal::doesEmailExist($data->email)) {
25+
$user = UserDal::getByEmail($data->email);
26+
27+
if ($user && password_verify($data->password, $user['password'])) {
28+
$userName = "{$user['first_name']} {$user['last_name']}";
29+
30+
$currentTime = time();
31+
$jwtToken = JWT::encode(
32+
[
33+
'iss' => $_ENV['APP_URL'],
34+
'iat' => $currentTime,
35+
'exp' => $currentTime + (60 * 60), // valid for an hour
36+
'data' => [
37+
'email' => $data->email,
38+
'name' => $userName
39+
]
40+
],
41+
$_ENV['JWT_KEY'],
42+
$_ENV['JWT_ALGO_ENCRYPTION']
43+
);
44+
45+
return [
46+
'message' => sprintf('%s successfully logged in', $userName),
47+
'token' => $jwtToken
48+
];
49+
}
50+
}
51+
throw new CredentialsInvalidException('Credentials invalid');
52+
}
53+
throw new InvalidValidationException('Payload invalid');
54+
}
55+
1856
public function create(mixed $data): array|object
1957
{
2058
$userValidation = new UserValidation($data);
@@ -102,7 +140,7 @@ public function retrieveAll(): array
102140
public function retrieve(string $userUuid): array
103141
{
104142
if (v::uuid()->validate($userUuid)) {
105-
if ($user = UserDal::get($userUuid)) {
143+
if ($user = UserDal::getById($userUuid)) {
106144
// Removing fields we don't want to expose
107145
unset($user['id']);
108146

src/Validation/Exception/InvalidValidationException.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
namespace PH7\ApiSimpleMenu\Validation\Exception;
44

5-
use RuntimeException;
5+
use InvalidArgumentException;
66

7-
class InvalidValidationException extends RuntimeException
7+
class InvalidValidationException extends InvalidArgumentException
88
{
99
}

src/Validation/UserValidation.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,13 @@ public function isUpdateSchemaValid(): bool
4141

4242
return $schemaValidation->validate($this->data);
4343
}
44+
45+
public function isLoginSchemaValid(): bool
46+
{
47+
$schemaValidation =
48+
v::attribute('email', v::stringType())
49+
->attribute('password', v::stringType());
50+
51+
return $schemaValidation->validate($this->data);
52+
}
4453
}

0 commit comments

Comments
 (0)