Improved jwt logic

* No longer stores full token, but only payload.
This commit is contained in:
Chris 2024-03-11 22:29:17 +01:00
parent 99b5710c59
commit 0513ab0999
5 changed files with 33 additions and 32 deletions

View File

@ -34,24 +34,24 @@ $ ./mercureact.phar -c mercureact.conf
## ToDos ## ToDos
* [ ] Read config from file * [x] Read config from file
* [ ] Security Security Security * [ ] Security Security Security
* [x] Check JWTs on connect * [x] Check JWTs on connect
* [x] Check claims on subscribe and publish * [x] Check claims on subscribe and publish
* [ ] WebSocket authentication * [x] Extract JWT claims to request attributes, instead of JWTToken
* [ ] Extract JWT claims to request attributes, instead of JWTToken
* [x] Subscription/Topic manager * [x] Subscription/Topic manager
* [x] Unify distribution * [x] Unify distribution
* [ ] Enumerate subscriptions and topics * [x] Enumerate subscriptions and topics
* [x] Publish events * [x] Publish events
* [x] Server-Side Events distributor * [x] Server-Side Events distributor
* [x] Distribute events over SSE * [x] Distribute events over SSE
* [ ] WebSocket distributor * [ ] WebSocket distributor
* [ ] WebSocket authentication
* [ ] Setup subscriptions * [ ] Setup subscriptions
* [ ] Dynamic subscriptions * [ ] Dynamic subscriptions
* [x] Distribute events over WS * [x] Distribute events over WS
* [x] Break out HTTP middleware into classes * [x] Break out HTTP middleware into classes
* [ ] HTTP middleware unittests * [ ] HTTP middleware unittests
* [ ] Replay missed events based on event id * [ ] Replay missed events based on event id
* [ ] Figure out how to determine last event IDs * [x] Figure out how to determine last event IDs
* [ ] Metrics endpoint * [ ] Metrics endpoint

View File

@ -26,7 +26,12 @@ class SseSubscriber implements SubscriberInterface
public function isAuthorized(): bool public function isAuthorized(): bool
{ {
return $this->request->getAttribute('authorization') instanceof JWTToken; return $this->request->getAttribute('authorized');
}
public function getPayload(): array
{
return $this->request->getAttribute('mercure.payload')??[];
} }
public function getId(): string public function getId(): string

View File

@ -69,7 +69,7 @@ class TopicManager
'topic' => $topic->getTopic(), 'topic' => $topic->getTopic(),
'subscriber' => $sub->getId(), 'subscriber' => $sub->getId(),
'active' => true, 'active' => true,
'payload' => null, // TODO populate from mercure.payload in JWT 'payload' => $sub->getPayload(),
]; ];
} }
} }

View File

@ -88,18 +88,14 @@ class MercureHandler
} }
// Grab the JWT token from the requests authorization attribute // Grab the JWT token from the requests authorization attribute
$tok = $request->getAttribute('authorization'); if ($request->getAttribute('authorized')) {
if ($tok instanceof JWTToken) { $claims = $request->getAttribute('mercure.subscribe');
$claims = $tok->claims->getAll(); if (!$this->checkTopicClaims($topics, $claims)) {
if (isset($claims['mercure']['subscribe'])) {
$subscribeClaims = $claims['mercure']['subscribe'];
if (!$this->checkTopicClaims($topics, $subscribeClaims)) {
throw new SecurityException( throw new SecurityException(
message: "Insufficient permissions for subscribe", message: "Insufficient permissions for subscribe",
code: SecurityException::ERR_NO_PERMISSION code: SecurityException::ERR_NO_PERMISSION
); );
} }
}
} else { } else {
// Disallow if we don't allow anonymous subscribers. Note that anonymous // Disallow if we don't allow anonymous subscribers. Note that anonymous
// subscribers will not receive updates marked as private. // subscribers will not receive updates marked as private.
@ -153,19 +149,15 @@ class MercureHandler
} }
// Grab the JWT token from the requests authorization attribute // Grab the JWT token from the requests authorization attribute
$tok = $request->getAttribute('authorization'); if ($request->getAttribute('authorized')) {
if ($tok instanceof JWTToken) { $claims = $request->getAttribute('mercure.publish');
$claims = $tok->claims->getAll();
if (isset($claims['mercure']['publish'])) {
$publishClaims = $claims['mercure']['publish'];
// check topic against publishClaims // check topic against publishClaims
if (!$this->checkTopicClaims($data['topic']??[], $publishClaims)) { if (!$this->checkTopicClaims($data['topic']??[], $claims)) {
throw new SecurityException( throw new SecurityException(
message: "Insufficient permissions for publish", message: "Insufficient permissions for publish",
code: SecurityException::ERR_NO_PERMISSION code: SecurityException::ERR_NO_PERMISSION
); );
} }
}
} else { } else {
// reject if access denied // reject if access denied
throw new SecurityException( throw new SecurityException(

View File

@ -61,11 +61,15 @@ class SecurityMiddleware
code: SecurityException::ERR_ACCESS_DENIED code: SecurityException::ERR_ACCESS_DENIED
); );
} }
$claims = $tok->claims->getAll()['mercure']??[];
return $request return $request
->withAttribute('authorization', $tok); ->withAttribute('mercure.publish', $claims['publish']??[])
->withAttribute('mercure.subscribe', $claims['subscribe']??[])
->withAttribute('mercure.payload', $claims['payload']??[])
->withAttribute('authorized', true);
} else { } else {
return $request return $request
->withAttribute('authorization', null); ->withAttribute('authorized', false);
} }
} }