New Symfony 7.1 CAS access token handler seems not to work #57390
Replies: 12 comments 2 replies
-
|
Hello, never used the |
Beta Was this translation helpful? Give feedback.
-
|
Hello, Any new? |
Beta Was this translation helpful? Give feedback.
-
|
I have the same issue. It seems this tokenAuthentication needs more documentation to be used, and perhaps more code to allow easy usage for developers. |
Beta Was this translation helpful? Give feedback.
-
|
I got the CAS access token working. Not sure it's the intended usage from the original author. Moreover, the configuration is missing also a snippet: You can find a working demo in my repo (adapt user from |
Beta Was this translation helpful? Give feedback.
-
|
Thank you very much @moobyfr for this comprehensive example. I previously solved my one problem with a custom mixed authentication based on phpCAS. But your solution is the simplest that can be written for a CAS only authentication, The symfony example propose two routes:
For those who want to try it out, here are the steps.
composer install
CAS_URL="https://cas.domain.tld/cas/"
CAS_VALIDATE_URL="https://cas.domain.tld/cas/serviceValidate"Il my case, I only had to update the two
Then add a new existing login (something jnown bu the CAS server)
symfony serveBut phpCAS also allows you to obtain additional attributes from the CAS server |
Beta Was this translation helpful? Give feedback.
-
|
My demo code isn't complete, the logout route is mandatory for easyadmin for example , I have a basic fix for it. The main problem is a 'while page' problem: when the service URL is redirected to another URL with 302, the authentication is broken. The problem had also happened with my demo application, but I wasn't sure to be able to reproduce it.
if I access again to the url without the ticket, all is ok |
Beta Was this translation helpful? Give feedback.
-
|
Hi, Thank you all, I'll try this soon ! |
Beta Was this translation helpful? Give feedback.
-
|
Well, thank you, I finally managed to implement native CAS authentication of Symfony 7.1.
###> cas ###
CAS_URL="https://auth.domain.tld/cas"
CAS_VALIDATE_PATH="/serviceValidate"
CAS_LOGIN_PATH="/login"
###< cas ###
###> cas ###
CAS_URL="https://my.domain.com/cas"
###< cas ###
parameters:
cas_url: '%env(CAS_URL)%'
cas_validate_url: '%cas_url%%env(CAS_VALIDATE_PATH)%'
cas_login_url: '%cas_url%%env(CAS_LOGIN_PATH)%'
services:
security.access_token_extractor.cas:
class: Symfony\Component\Security\Http\AccessToken\QueryAccessTokenExtractor
arguments:
- 'ticket'
App\Security\CasAuthenticatorEntryPoint:
arguments:
$casLoginUrl: '%cas_login_url%'
security:
firewalls:
main:
pattern: ^/
provider: app_user_provider
access_token:
token_handler:
cas:
validation_url: '%cas_validate_url%'
token_extractors:
- security.access_token_extractor.cas
entry_point: App\Security\CasAuthenticatorEntryPoint
<?php
namespace App\Security;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
class CasAuthenticatorEntryPoint implements AuthenticationEntryPointInterface
{
private string $casLoginUrl;
public function __construct(string $casLoginUrl)
{
$this->casLoginUrl = $casLoginUrl;
}
public function start(Request $request, AuthenticationException $authException = null): Response
{
$redirectUrl = sprintf('%s?service=%s', $this->casLoginUrl, urlencode($request->getUri()));
return new RedirectResponse($redirectUrl);
}
}Authentication works well but once logged in, the CAS ticket is retained in the URI.
<?php
namespace App\Security;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
#[AsEventListener(event: LoginSuccessEvent::class, method: 'onLoginSuccessEvent')]
final class CasAuthenticatorListener
{
public function onLoginSuccessEvent(LoginSuccessEvent $event): void
{
$request = $event->getRequest();
$scheme = $request->getScheme();
$port = $request->getPort();
$queryParams = $request->query->all();
$portString = '';
if (
($scheme === 'http' && $port !== 80) ||
($scheme === 'https' && $port !== 443)
) {
$portString = ':'.$port;
}
unset($queryParams['ticket']);
$queryString = count($queryParams) ? '?'.http_build_query($queryParams) : '';
$uri = sprintf(
'%s://%s%s%s%s',
$scheme,
$request->getHost(),
$portString,
$request->getPathInfo(),
$queryString
);
$event->setResponse(new RedirectResponse($uri));
}
}Thanks again ! |
Beta Was this translation helpful? Give feedback.
-
|
Simplified version of <?php
namespace App\Security;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
#[AsEventListener(event: LoginSuccessEvent::class, method: 'onLoginSuccessEvent')]
final class CasAuthenticatorListener
{
public function onLoginSuccessEvent(LoginSuccessEvent $event): void
{
$request = $event->getRequest();
$request->query->remove('ticket');
$request->overrideGlobals();
$event->setResponse(new RedirectResponse($request->getUri()));
}
} |
Beta Was this translation helpful? Give feedback.
-
|
I've updated the demo application with your code, thank you! |
Beta Was this translation helpful? Give feedback.
-
|
As @diam said, CAS also allows you to obtain additional attributes from the CAS server (for example, firstname, lastname, email, etc.). $event->getAuthenticatedToken()->getAttributes()This array is empty. |
Beta Was this translation helpful? Give feedback.
-
|
- reply to myself - I successfully retrieve additional attributes from the CAS server with this patch :
/**
* @throws AuthenticationException
*/
public function getUserBadgeFrom(string $accessToken): UserBadge
{
$response = $this->client->request('GET', $this->getValidationUrl($accessToken));
$xml = new \SimpleXMLElement($response->getContent(), 0, false, $this->prefix, true);
if (isset($xml->authenticationSuccess)) {
// START PATCH
// - before -
// return new UserBadge((string) $xml->authenticationSuccess->user);
// - after -
$attributes = isset($xml->authenticationSuccess->attributes) ? (array) $xml->authenticationSuccess->attributes : [];
return new UserBadge((string) $xml->authenticationSuccess->user, null, $attributes);
// END PATCH
}
if (isset($xml->authenticationFailure)) {
throw new AuthenticationException('CAS Authentication Failure: '.trim((string) $xml->authenticationFailure));
}
throw new AuthenticationException('Invalid CAS response.');
}We can now get attributes from listener : [...]
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
[...]
public function onLoginSuccessEvent(LoginSuccessEvent $event): void
{
$attributes = $event->getPassport()->getBadge(UserBadge::class)->getAttributes();
[...]
}But I don't know if the |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi,
On this page, it is indicated that "Symfony 7.1 introduces a new CAS 2.0 access token handler".
I can't implement it.
I'm using Symfony 7.1.1 :
security.yaml:https://auth.domain.ltd/cas/validateis actually the real validation URL of my CAS server.Result is code 401, I'm not redirected to the authentication form of CAS server :
My
App\Entity\Userentity was just generated withbin/console make:user.If I disable access control rule, I see the controller response so route is working well.
Maybe I forgot something ?
Can anybody help me ?
Beta Was this translation helpful? Give feedback.
All reactions