Multi domain usage
Closed this issue · 1 comments
floviolleau commented
Hi,
Thank for this great project. We have an issue:
- website have multiple domains for the same code/server: domain.tld, anotherdomain.tld2, newdomain.tld3
- we have one mercure instance: mercuredomain.com
What value can we set in mercure config of the bundle with multiple domain to avoid Unable to create authorization cookie for a hub on the different second-level domain
?
Thanks
floviolleau commented
Hi,
What we did:
- kept domains we set
- created mercure.domain.tld, mercure.anotherdomain.tld2, mercure.newdomain.tld3
- kept one mercure instance: mercuredomain.com but configured domain above to point to this instance
- added in mercure_extra_directives all domains that consume mercure: domain.tld, anotherdomain.tld2, newdomain.tld3
- added in
mercure.yaml
:
mercure:
hubs:
# Default Hub config which is used to configure Hub instance we get when injecting HubInterface like in our MercureManager
# This is where mercure update our publish by our symfony app
default:
url: 'https://mercuredomain.com'
public_url: 'https://mercuredomain.com'
jwt:
secret: '%env(MERCURE_JWT_SECRET)%'
publish: '*'
algorithm: 'hmac.sha256'
# Define one hub by domain name to allow setting mercure auth cookie on these domain inside MercureAuthorizationManager
# Only "public_url" value change
domain_tld:
url: 'https://mercuredomain.com'
public_url: 'https://mercure.domain.tld/.well-known/mercure'
jwt:
secret: '%env(MERCURE_JWT_SECRET)%'
publish: '*'
algorithm: 'hmac.sha256'
anotherdomain_tld2:
url: 'https://mercuredomain.com'
public_url: 'https://mercure.anotherdomain.tld2/.well-known/mercure'
jwt:
secret: '%env(MERCURE_JWT_SECRET)%'
publish: '*'
algorithm: 'hmac.sha256'
newdomain_tld3:
url: 'https://mercuredomain.com'
public_url: 'https://mercure.newdomain.tld3/.well-known/mercure'
jwt:
secret: '%env(MERCURE_JWT_SECRET)%'
publish: '*'
algorithm: 'hmac.sha256'
In our MercureAuthorizationManager.php
<?php
namespace App\Security;
use App\Entity\User\User;
use DateTimeImmutable;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Mercure\Authorization;
use Symfony\Component\Mercure\HubRegistry;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Security;
/**
* MercureAuthorizationManager.
*/
class MercureAuthorizationManager
{
/**
* @var Authorization
*/
protected Authorization $mercureAutorization;
/**
* @var RouterInterface
*/
protected RouterInterface $router;
/**
* @var Security
*/
protected Security $security;
/**
* @var HubRegistry
*/
protected HubRegistry $hubRegistry;
/**
* @var Request
*/
protected Request $currentRequest;
/**
* Constructor.
*/
public function __construct(
Authorization $mercureAutorization,
RouterInterface $router,
Security $security,
HubRegistry $hubRegistry,
RequestStack $requestStack
) {
$this->mercureAutorization = $mercureAutorization;
$this->router = $router;
$this->security = $security;
$this->hubRegistry = $hubRegistry;
$this->currentRequest = $requestStack->getCurrentRequest();
}
/**
* Add Mercure Authorization Cookie to request.
*
* @param Request $request
* @param User|null $user
* @return void
*/
public function addMercureAuthorizationCookieToRequest(Request $request, User $user = null)
{
/** @var User $currentUser */
$currentUser = $user ?: $this->security->getUser();
// Generate allowed subscribe topics
$subscribeTopics = $this->generateAllowedSubscribeTopics($user);
// Generate allowed publish topics
$publishTopics = $this->generateAllowedPublishTopics($user);
// Get hub name from current request domain name to set cookie on the proper mercure domain name
// (this value only change from "default" hub in production, in other env, mercure is on the same
// domain name than symfony app so we do not have issue to create cookie)
$hubName = $this->getMercureHubNameFromCurrentRequestDomainName($request);
// Add Mercure Authorization Cookie to request
$this->mercureAutorization->setCookie($request, $subscribeTopics, $publishTopics, [
// Fixe years expiration date for the moment to avoid having to refresh jwt token
// But we will create an expiration system
'exp' => new DateTimeImmutable('+5 years'),
'currentUserId' => $currentUser->getId(),
], $hubName);
}
/**
* Generate allowed subscribe topics.
*
* YOU NEED TO ADD HERE THE TOPICS YOU WANT THE FRONT TO BE ABLE TO SUBSCRIBE ON.
*
* @param User $user
* @return array
*/
protected function generateAllowedSubscribeTopics(User $user): array
{
$userId = $user->getId();
$topics = [
// Async Task progress
$this->router->generate('task_daily_progress', [
'user' => $userId,
], UrlGeneratorInterface::ABSOLUTE_PATH),
// Web Notification
$this->router->generate('user_notifications_web', [
'user' => $userId,
], UrlGeneratorInterface::ABSOLUTE_PATH),
];
return $topics;
}
/**
* Generate allowed publish topics.
*
* YOU NEED TO ADD HERE THE TOPICS YOU WANT THE FRONT TO BE ABLE TO PUBLISH ON.
*
* @param User $user
* @return array
*/
protected function generateAllowedPublishTopics(User $user): array
{
$topics = [];
return $topics;
}
/**
* Get hub name from current request domain name.
*
* See mercure.yaml for hub config.
*
* @param Request $request
* @return string
*/
protected function getMercureHubNameFromCurrentRequestDomainName(Request $request): string
{
switch ($request->getHost()) {
case 'domain.tld':
return 'domain_tld';
case 'anotherdomain.tld2':
return 'anotherdomain_tld2';
case 'newdomain.tld3':
return 'newdomain_tld3';
default:
return 'default';
}
}
/**
* Get mercure public url to use by our front.
*
* (used inside base.html.twig)
* @return string
*/
public function getMercurePublicUrl()
{
return $this->hubRegistry->getHub(
$this->getMercureHubNameFromCurrentRequestDomainName($this->currentRequest)
)->getPublicUrl();
}
}
Hope that it can help so..