Skip to content

Commit 67a91c4

Browse files
Refactor JwtAuthenticator to use User Oxid
1 parent a55c9b8 commit 67a91c4

18 files changed

Lines changed: 539 additions & 161 deletions

README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ Response:
5454
{
5555
"token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
5656
"user": {
57-
"id": "abc123",
5857
"username": "user@example.com",
5958
"roles": ["ROLE_USER"]
6059
}
@@ -97,7 +96,6 @@ use Symfony\Component\Security\Http\Attribute\CurrentUser;
9796
public function getData(#[CurrentUser] ApiUser $user): Response
9897
{
9998
return new JsonResponse([
100-
'user_id' => $user->getUserId(),
10199
'username' => $user->getUserIdentifier(),
102100
'roles' => $user->getRoles()
103101
]);

src/Security/AdminController.php

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,11 @@
1818

1919
final readonly class AdminController
2020
{
21-
public function __construct(
22-
private int $apiRateLimit
23-
) {
24-
}
25-
2621
#[Route('/api/admin/settings', methods: ['GET'])]
2722
#[IsGranted('ROLE_ADMIN')]
2823
public function getSettings(#[CurrentUser] ApiUser $user): Response
2924
{
3025
return new JsonResponse([
31-
'settings' => [
32-
'api_rate_limit' => $this->apiRateLimit,
33-
],
3426
'admin_user' => $user->getUserIdentifier(),
3527
]);
3628
}

src/Security/Auth/AuthenticationSuccessHandler.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,14 @@ public function onAuthenticationSuccess(Request $request, TokenInterface $token)
2828
$user = $token->getUser();
2929

3030
if (!$user instanceof ApiUser) {
31-
return new JsonResponse(['error' => 'Invalid user type'], Response::HTTP_INTERNAL_SERVER_ERROR);
31+
return new JsonResponse(['error' => 'Invalid user type'], Response::HTTP_UNAUTHORIZED);
3232
}
3333

3434
$jwtToken = $this->tokenService->generateToken($user);
3535

3636
return new JsonResponse([
3737
'token' => $jwtToken,
3838
'user' => [
39-
'id' => $user->getUserId(),
4039
'username' => $user->getUserIdentifier(),
4140
'roles' => $user->getRoles()
4241
]

src/Security/Auth/JwtAuthenticator.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
use Symfony\Component\HttpFoundation\Request;
1414
use Symfony\Component\HttpFoundation\Response;
1515
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
16+
use OxidEsales\AuthComponent\Security\User\ApiUserProvider;
1617
use Symfony\Component\Security\Core\Exception\AuthenticationException;
17-
use Symfony\Component\Security\Core\User\UserProviderInterface;
1818
use Symfony\Component\Security\Http\AccessToken\AccessTokenExtractorInterface;
1919
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
2020
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
@@ -25,7 +25,7 @@ class JwtAuthenticator extends AbstractAuthenticator
2525
{
2626
public function __construct(
2727
private readonly TokenServiceInterface $tokenService,
28-
private readonly UserProviderInterface $userProvider,
28+
private readonly ApiUserProvider $userProvider,
2929
private readonly AccessTokenExtractorInterface $tokenExtractor,
3030
) {
3131
}
@@ -45,10 +45,14 @@ public function authenticate(Request $request): Passport
4545

4646
$parsedToken = $this->tokenService->parseToken($token);
4747

48-
$username = $parsedToken->claims()->get('username');
48+
$userId = $parsedToken->claims()->get('userId');
49+
50+
if (!is_string($userId) || $userId === '') {
51+
throw new AuthenticationException('Token missing userId claim');
52+
}
4953

5054
return new SelfValidatingPassport(
51-
new UserBadge($username, fn() => $this->userProvider->loadUserByIdentifier($username))
55+
new UserBadge($userId, fn() => $this->userProvider->loadByOxid($userId))
5256
);
5357
}
5458

src/Security/Auth/TokenService.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public function generateToken(ApiUser $user): string
7070
->identifiedBy(bin2hex(random_bytes(16)))
7171
->issuedAt($now)
7272
->expiresAt($now->modify(sprintf('+%d seconds', $this->expirationSeconds)))
73-
->withClaim('username', $user->getUserIdentifier())
73+
->withClaim('userId', $user->getOxid())
7474
->getToken($this->config->signer(), $this->config->signingKey());
7575

7676
return $token->toString();

src/Security/ProfileController.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
public function getProfile(#[CurrentUser] ApiUser $user): Response
2424
{
2525
return new JsonResponse([
26-
'id' => $user->getUserId(),
2726
'username' => $user->getUserIdentifier(),
2827
'roles' => $user->getRoles()
2928
]);

src/Security/User/ApiUser.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,21 @@
1515
final class ApiUser implements UserInterface, PasswordAuthenticatedUserInterface
1616
{
1717
public function __construct(
18-
private readonly string $userId,
18+
private readonly string $oxid,
1919
private readonly string $username,
2020
private readonly array $roles,
21-
private ?string $password = null
21+
#[\SensitiveParameter] private ?string $password = null
2222
) {
2323
}
2424

25-
public function getUserIdentifier(): string
25+
public function getOxid(): string
2626
{
27-
return $this->username;
27+
return $this->oxid;
2828
}
2929

30-
public function getUserId(): string
30+
public function getUserIdentifier(): string
3131
{
32-
return $this->userId;
32+
return $this->username;
3333
}
3434

3535
public function getRoles(): array

src/Security/User/ApiUserProvider.php

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
public function __construct(
2222
private QueryBuilderFactoryInterface $queryBuilderFactory,
2323
private RoleResolverInterface $roleResolver,
24-
private ContextInterface $context
24+
private ContextInterface $context,
2525
) {
2626
}
2727

@@ -44,14 +44,29 @@ public function loadUserByIdentifier(string $identifier): UserInterface
4444
throw new UserNotFoundException(sprintf('User "%s" not found.', $identifier));
4545
}
4646

47-
$roles = $this->roleResolver->resolveRoles($userData['OXRIGHTS']);
47+
return $this->createApiUser($userData);
48+
}
4849

49-
return new ApiUser(
50-
$userData['OXID'],
51-
$userData['OXUSERNAME'],
52-
$roles,
53-
$userData['OXPASSWORD']
54-
);
50+
public function loadByOxid(string $oxid): UserInterface
51+
{
52+
$queryBuilder = $this->queryBuilderFactory->create();
53+
$queryBuilder
54+
->select('OXID', 'OXUSERNAME', 'OXPASSWORD', 'OXRIGHTS')
55+
->from('oxuser')
56+
->where('OXID = :userId')
57+
->andWhere('OXACTIVE = 1')
58+
->andWhere('(OXSHOPID = :shopId OR OXRIGHTS = :mallAdmin)')
59+
->setParameter('userId', $oxid)
60+
->setParameter('shopId', $this->context->getCurrentShopId())
61+
->setParameter('mallAdmin', 'malladmin');
62+
63+
$userData = $queryBuilder->execute()->fetchAssociative();
64+
65+
if (!$userData) {
66+
throw new UserNotFoundException(sprintf('User "%s" not found.', $oxid));
67+
}
68+
69+
return $this->createApiUser($userData);
5570
}
5671

5772
public function refreshUser(UserInterface $user): UserInterface
@@ -60,11 +75,23 @@ public function refreshUser(UserInterface $user): UserInterface
6075
throw new UnsupportedUserException();
6176
}
6277

63-
return $this->loadUserByIdentifier($user->getUserIdentifier());
78+
return $this->loadByOxid($user->getOxid());
6479
}
6580

6681
public function supportsClass(string $class): bool
6782
{
6883
return ApiUser::class === $class;
6984
}
85+
86+
private function createApiUser(array $userData): ApiUser
87+
{
88+
$roles = $this->roleResolver->resolveRoles($userData['OXRIGHTS']);
89+
90+
return new ApiUser(
91+
$userData['OXID'],
92+
$userData['OXUSERNAME'],
93+
$roles,
94+
$userData['OXPASSWORD']
95+
);
96+
}
7097
}

src/Security/services.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ services:
1515

1616
OxidEsales\AuthComponent\Security\AdminController:
1717
public: true
18-
arguments:
19-
$apiRateLimit: '%oxid_esales.rate_limiter.limit%'
2018

2119
Symfony\Component\Security\Http\HttpUtils: ~
2220

tests/Fixtures/TestModule/TestApiController.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ public function userEndpoint(#[CurrentUser] ApiUser $user): Response
3434
return new JsonResponse([
3535
'message' => 'User endpoint - ROLE_USER required',
3636
'user' => [
37-
'id' => $user->getUserId(),
3837
'username' => $user->getUserIdentifier(),
3938
'roles' => $user->getRoles(),
4039
],
@@ -49,7 +48,6 @@ public function adminEndpoint(#[CurrentUser] ApiUser $user): Response
4948
return new JsonResponse([
5049
'message' => 'Admin endpoint - ROLE_ADMIN required',
5150
'user' => [
52-
'id' => $user->getUserId(),
5351
'username' => $user->getUserIdentifier(),
5452
'roles' => $user->getRoles(),
5553
],
@@ -64,7 +62,6 @@ public function infoEndpoint(#[CurrentUser] ?ApiUser $user): Response
6462
'message' => 'Info endpoint - optional authentication',
6563
'authenticated' => $user !== null,
6664
'user' => $user ? [
67-
'id' => $user->getUserId(),
6865
'username' => $user->getUserIdentifier(),
6966
'roles' => $user->getRoles(),
7067
] : null,

0 commit comments

Comments
 (0)