I am using Slim 4 with PHP 7.3 on a Debian 10 VM instance in the Google Cloud.
I have written a custom Auth Handler and Auth Controller that relies of Microsoft MSAL and Graph for User Authentication.
Whenever the authentication process cycles through the MSAL auth process, it sends the browser back to the /auth/callback route. The redirect in this route gets a hash character appended to it. I have added logging immediately before the redirect, and the URL variable $redirectURL
does not contain the hash character.
Below are the relevant snippets of my code.
UserAuthMiddleware
class UserAuthMiddleware implements Middleware
{
private $session;
private $user;
public function __construct(Session $session, User $user)
{
$this->session = $session;
$this->user = $user;
}
public function __invoke(Request $request, Handler $handler): Response
{
return $this->process($request, $handler);
}
public function process(Request $request, Handler $handler): Response
{
if ($this->session->exists('user') && false !== $this->session->get('user')) {
$request = $request->withAttribute('user', $this->user);
return $handler->handle($request);
}
$this->session->set('userauthroute', $request->getUri()->getPath());
$factory = new ResponseFactory;
$response = $factory->createResponse();
return $response
->withStatus(302)
->withHeader('Location', RouteContext::fromRequest($request)->getRouteParser()->urlFor('login'));
}
}
UserAuthController
class UserAuthController
{
private $container;
private $responseFactory;
private $session;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
$this->responseFactory = new ResponseFactory();
$this->session = $container->get('session');
}
public function login(Request $request, Response $response, $args): Response
{
$provider = $this->getProvider($request);
$authURL = $provider->getAuthorizationUrl(['scope' => ['User.Read']]);
$this->session->set('oauth2state', $provider->getState());
$this->session->set('user', false);
return $this->responseFactory->createResponse()
->withHeader('Location', $authURL)
->withStatus(302);
}
public function callback(Request $request, Response $response, $args): Response
{
$params = $request->getQueryParams();
if (!array_key_exists('code', $params) || !array_key_exists('state', $params)) {
throw new UserAuthException("Invalid Callback Request");
}
if (!$this->session->exists('oauth2state') || $this->session->get('oauth2state') !== $params['state']) {
throw new UserAuthException("Callback State Error");
}
$this->session->delete('oauth2state');
$provider = $this->getProvider($request);
$token = $provider->getAccessToken('authorization_code', ['code' => $params['code']]);
$graph = new Graph();
$graph->setAccessToken($token->getToken());
$user = $graph->createRequest("GET", "/me")->setReturnType(Model\User::class)->execute();
$this->session->set('user', $user->getMail());
if ($this->session->exists('userauthroute')) {
$redirectURL = $this->session->get('userauthroute');
$this->session->delete('userauthroute');
} else {
$redirectURL = RouteContext::fromRequest($request)->getRouteParser()->urlFor('home');
}
$logger = LoggerFactory::create('info');
$logger->info($redirectURL);
return $this->responseFactory->createResponse()
->withHeader('Location', $redirectURL)
->withStatus(302);
}
public function logout(Request $request, Response $response, $args): Response
{
$this->session->set('user', false);
return $this->container->get('view')->render($response, 'logout.twig');
}
private function getProvider($request): Provider
{
$uri = $request->getUri();
$callbackUrl = RouteContext::fromRequest($request)->getRouteParser()->fullUrlFor($uri, 'callback');
$options = array_merge($this->container->get('MSAL_Config'), ['redirectUrl' => $callbackUrl]);
return new Provider($options);
}
}
routes.php
// MSAL User Auth Routes
$app->group('/auth', function (RouteCollectorProxy $group) use ($app) {
$group->get('/login', [UserAuthController::class, 'login'])->setName('login');
$group->get('/callback', [UserAuthController::class, 'callback'])->setName('callback');
$group->get('/logout', [UserAuthController::class, 'logout'])->setName('logout')->add(TwigMiddleware::createFromContainer($app));
});
I will be grateful if anyone can help me figure out why and where the hash character is being appended, and what I can do to prevent this from happening.