diff --git a/.version b/.version
index 91ff572..26d99a2 100644
--- a/.version
+++ b/.version
@@ -1 +1 @@
-5.2.0
+5.2.1
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 24082bb..cd94f73 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# Change Log
+## [5.2.1](https://github.com/auth0/symfony/tree/5.2.1) (2023-12-16)
+[Full Changelog](https://github.com/auth0/symfony/compare/5.2.0...5.2.1)
+
+**Fixed**
+- Restore method signatures [\#174](https://github.com/auth0/symfony/pull/174) ([evansims](https://github.com/evansims))
+
## [5.2.0](https://github.com/auth0/symfony/tree/5.2.0) (2023-12-12)
**Added**
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
index dff6ccd..3beb255 100644
--- a/phpstan.neon.dist
+++ b/phpstan.neon.dist
@@ -19,6 +19,8 @@ parameters:
- '#Cannot call method purge\(\) on Auth0\\SDK\\Contract\\StoreInterface\|null.#'
- '#Casting to string something that(.*) already string.#'
- '#\$object_or_class of function method_exists expects object\|string, (.*) given.#'
+ - '#Property (.*) is never read, only written.#'
+ - '#Call to function is_string\(\) with string will always evaluate to true.$#'
-
message: '#Parameter \#3 \$(.*) of function openssl_verify expects (.*), (.*) given.#'
path: src\Token\Verifier.php
@@ -48,3 +50,4 @@ parameters:
path: src\Utility\HttpRequest.php
reportUnmatchedIgnoredErrors: false
+ checkGenericClassInNonGenericObjectType: false
diff --git a/psalm.xml.dist b/psalm.xml.dist
index d36109b..6a528d4 100644
--- a/psalm.xml.dist
+++ b/psalm.xml.dist
@@ -10,10 +10,6 @@
>
-
-
-
-
diff --git a/src/Auth0Bundle.php b/src/Auth0Bundle.php
index ca603b5..59127b7 100644
--- a/src/Auth0Bundle.php
+++ b/src/Auth0Bundle.php
@@ -5,11 +5,17 @@
namespace Auth0\Symfony;
use Auth0\SDK\Configuration\SdkConfiguration;
+use Auth0\SDK\Contract\StoreInterface;
use Auth0\SDK\Token;
use Auth0\Symfony\Contracts\BundleInterface;
use Auth0\Symfony\Controllers\AuthenticationController;
use Auth0\Symfony\Security\{Authenticator, Authorizer, UserProvider};
use Auth0\Symfony\Stores\SessionStore;
+use OpenSSLAsymmetricKey;
+use Psr\Cache\CacheItemPoolInterface;
+use Psr\EventDispatcher\ListenerProviderInterface;
+use Psr\Http\Client\ClientInterface;
+use Psr\Http\Message\{RequestFactoryInterface, ResponseFactoryInterface, StreamFactoryInterface};
use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\DependencyInjection\{ContainerBuilder, Reference};
@@ -22,56 +28,84 @@ public function configure(DefinitionConfigurator $definition): void
$definition->import('../config/definition.php');
}
+ /**
+ * @param array $config The configuration array.
+ * @param ContainerConfigurator $container The container configurator.
+ * @param ContainerBuilder $builder The container builder.
+ */
public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
{
- $tokenCache = $config['sdk']['token_cache'] ?? 'cache.app';
- $tokenCache = new Reference($tokenCache);
+ $sdkConfig = $config['sdk'] ?? [];
- $managementTokenCache = $config['sdk']['management_token_cache'] ?? 'cache.app';
- $managementTokenCache = new Reference($managementTokenCache);
+ /**
+ * @var array{strategy: string, domain: ?string, custom_domain: ?string, client_id: ?string, redirect_uri: ?string, client_secret: ?string, audiences: null|array, organizations: null|array, use_pkce: bool, scopes: null|array, response_mode: string, response_type: string, token_algorithm: ?string, token_jwks_uri: ?string, token_max_age: ?int, token_leeway: ?int, token_cache: ?CacheItemPoolInterface, token_cache_ttl: int, http_client: null|ClientInterface|string, http_max_retries: int, http_request_factory: null|RequestFactoryInterface|string, http_response_factory: null|ResponseFactoryInterface|string, http_stream_factory: null|StreamFactoryInterface|string, http_telemetry: bool, session_storage: ?StoreInterface, session_storage_prefix: ?string, cookie_secret: ?string, cookie_domain: ?string, cookie_expires: int, cookie_path: string, cookie_secure: bool, cookie_same_site: ?string, persist_user: bool, persist_id_token: bool, persist_access_token: bool, persist_refresh_token: bool, transient_storage: ?StoreInterface, transient_storage_prefix: ?string, query_user_info: bool, management_token: ?string, management_token_cache: ?CacheItemPoolInterface, event_listener_provider: null|ListenerProviderInterface|string, client_assertion_signing_key: null|OpenSSLAsymmetricKey|string, client_assertion_signing_algorithm: string, pushed_authorization_request: bool, backchannel_logout_cache: ?CacheItemPoolInterface, backchannel_logout_expires: int} $sdkConfig
+ */
+ $tokenCache = $sdkConfig['token_cache'] ?? 'cache.app';
- $backchannelLogoutCache = $config['sdk']['backchannel_logout_cache'] ?? 'cache.app';
- $backchannelLogoutCache = new Reference($backchannelLogoutCache);
+ if (! $tokenCache instanceof CacheItemPoolInterface) {
+ $tokenCache = new Reference($tokenCache);
+ }
+
+ $managementTokenCache = $sdkConfig['management_token_cache'] ?? 'cache.app';
+
+ if (! $managementTokenCache instanceof CacheItemPoolInterface) {
+ $managementTokenCache = new Reference($managementTokenCache);
+ }
+
+ $backchannelLogoutCache = $sdkConfig['backchannel_logout_cache'] ?? 'cache.app';
+
+ if (! $backchannelLogoutCache instanceof CacheItemPoolInterface) {
+ $backchannelLogoutCache = new Reference($backchannelLogoutCache);
+ }
+
+ $transientStorage = $sdkConfig['transient_storage'] ?? 'auth0.store_transient';
+
+ if (! $transientStorage instanceof StoreInterface) {
+ $transientStorage = new Reference($transientStorage);
+ }
+
+ $sessionStorage = $sdkConfig['session_storage'] ?? 'auth0.store_session';
- $transientStorage = new Reference($config['sdk']['transient_storage'] ?? 'auth0.store_transient');
- $sessionStorage = new Reference($config['sdk']['session_storage'] ?? 'auth0.store_session');
+ if (! $sessionStorage instanceof StoreInterface) {
+ $sessionStorage = new Reference($sessionStorage);
+ }
- $transientStoragePrefix = $config['sdk']['transient_storage_prefix'] ?? 'auth0_transient';
- $sessionStoragePrefix = $config['sdk']['session_storage_prefix'] ?? 'auth0_session';
+ $transientStoragePrefix = $sdkConfig['transient_storage_prefix'] ?? 'auth0_transient';
+ $sessionStoragePrefix = $sdkConfig['session_storage_prefix'] ?? 'auth0_session';
- $eventListenerProvider = $config['sdk']['event_listener_provider'] ?? null;
+ $eventListenerProvider = $sdkConfig['event_listener_provider'] ?? null;
- if (null !== $eventListenerProvider && '' !== $eventListenerProvider) {
+ if (! $eventListenerProvider instanceof ListenerProviderInterface && '' !== $eventListenerProvider && null !== $eventListenerProvider) {
$eventListenerProvider = new Reference($eventListenerProvider);
}
- $httpClient = $config['sdk']['http_client'] ?? null;
+ $httpClient = $sdkConfig['http_client'] ?? null;
- if (null !== $httpClient && '' !== $httpClient) {
+ if (! $httpClient instanceof ClientInterface && '' !== $httpClient && null !== $httpClient) {
$httpClient = new Reference($httpClient);
}
- $httpRequestFactory = $config['sdk']['http_request_factory'] ?? null;
+ $httpRequestFactory = $sdkConfig['http_request_factory'] ?? null;
- if (null !== $httpRequestFactory && '' !== $httpRequestFactory) {
+ if (! $httpRequestFactory instanceof RequestFactoryInterface && '' !== $httpRequestFactory && null !== $httpRequestFactory) {
$httpRequestFactory = new Reference($httpRequestFactory);
}
- $httpResponseFactory = $config['sdk']['http_response_factory'] ?? null;
+ $httpResponseFactory = $sdkConfig['http_response_factory'] ?? null;
- if (null !== $httpResponseFactory && '' !== $httpResponseFactory) {
+ if (! $httpResponseFactory instanceof ResponseFactoryInterface && '' !== $httpResponseFactory && null !== $httpResponseFactory) {
$httpResponseFactory = new Reference($httpResponseFactory);
}
- $httpStreamFactory = $config['sdk']['http_stream_factory'] ?? null;
+ $httpStreamFactory = $sdkConfig['http_stream_factory'] ?? null;
- if (null !== $httpStreamFactory && '' !== $httpStreamFactory) {
+ if (! $httpStreamFactory instanceof StreamFactoryInterface && '' !== $httpStreamFactory && null !== $httpStreamFactory) {
$httpStreamFactory = new Reference($httpStreamFactory);
}
- $audiences = $config['sdk']['audiences'] ?? [];
- $organizations = $config['sdk']['organizations'] ?? [];
- $scopes = $config['sdk']['scopes'] ?? [];
+ $audiences = $sdkConfig['audiences'] ?? [];
+ $organizations = $sdkConfig['organizations'] ?? [];
+ $scopes = $sdkConfig['scopes'] ?? [];
if ([] === $audiences) {
$audiences = null;
@@ -88,38 +122,38 @@ public function loadExtension(array $config, ContainerConfigurator $container, C
$container->services()
->set('auth0.configuration', SdkConfiguration::class)
->arg('$configuration', null)
- ->arg('$strategy', $config['sdk']['strategy'])
- ->arg('$domain', $config['sdk']['domain'])
- ->arg('$customDomain', $config['sdk']['custom_domain'])
- ->arg('$clientId', $config['sdk']['client_id'])
- ->arg('$redirectUri', $config['sdk']['redirect_uri'])
- ->arg('$clientSecret', $config['sdk']['client_secret'])
+ ->arg('$strategy', $sdkConfig['strategy'])
+ ->arg('$domain', $sdkConfig['domain'])
+ ->arg('$customDomain', $sdkConfig['custom_domain'])
+ ->arg('$clientId', $sdkConfig['client_id'])
+ ->arg('$redirectUri', $sdkConfig['redirect_uri'])
+ ->arg('$clientSecret', $sdkConfig['client_secret'])
->arg('$audience', $audiences)
->arg('$organization', $organizations)
->arg('$usePkce', true)
->arg('$scope', $scopes)
->arg('$responseMode', 'query')
->arg('$responseType', 'code')
- ->arg('$tokenAlgorithm', $config['sdk']['token_algorithm'] ?? Token::ALGO_RS256)
- ->arg('$tokenJwksUri', $config['sdk']['token_jwks_uri'])
- ->arg('$tokenMaxAge', $config['sdk']['token_max_age'])
- ->arg('$tokenLeeway', $config['sdk']['token_leeway'] ?? 60)
+ ->arg('$tokenAlgorithm', $sdkConfig['token_algorithm'] ?? Token::ALGO_RS256)
+ ->arg('$tokenJwksUri', $sdkConfig['token_jwks_uri'])
+ ->arg('$tokenMaxAge', $sdkConfig['token_max_age'])
+ ->arg('$tokenLeeway', $sdkConfig['token_leeway'] ?? 60)
->arg('$tokenCache', $tokenCache)
- ->arg('$tokenCacheTtl', $config['sdk']['token_cache_ttl'])
+ ->arg('$tokenCacheTtl', $sdkConfig['token_cache_ttl'])
->arg('$httpClient', $httpClient)
- ->arg('$httpMaxRetries', $config['sdk']['http_max_retries'])
+ ->arg('$httpMaxRetries', $sdkConfig['http_max_retries'])
->arg('$httpRequestFactory', $httpRequestFactory)
->arg('$httpResponseFactory', $httpResponseFactory)
->arg('$httpStreamFactory', $httpStreamFactory)
- ->arg('$httpTelemetry', $config['sdk']['http_telemetry'])
+ ->arg('$httpTelemetry', $sdkConfig['http_telemetry'])
->arg('$sessionStorage', $sessionStorage)
->arg('$sessionStorageId', $sessionStoragePrefix)
- ->arg('$cookieSecret', $config['sdk']['cookie_secret'])
- ->arg('$cookieDomain', $config['sdk']['cookie_domain'])
- ->arg('$cookieExpires', $config['sdk']['cookie_expires'])
- ->arg('$cookiePath', $config['sdk']['cookie_path'])
- ->arg('$cookieSameSite', $config['sdk']['cookie_same_site'])
- ->arg('$cookieSecure', $config['sdk']['cookie_secure'])
+ ->arg('$cookieSecret', $sdkConfig['cookie_secret'])
+ ->arg('$cookieDomain', $sdkConfig['cookie_domain'])
+ ->arg('$cookieExpires', $sdkConfig['cookie_expires'])
+ ->arg('$cookiePath', $sdkConfig['cookie_path'])
+ ->arg('$cookieSameSite', $sdkConfig['cookie_same_site'])
+ ->arg('$cookieSecure', $sdkConfig['cookie_secure'])
->arg('$persistUser', true)
->arg('$persistIdToken', true)
->arg('$persistAccessToken', true)
@@ -127,11 +161,11 @@ public function loadExtension(array $config, ContainerConfigurator $container, C
->arg('$transientStorage', $transientStorage)
->arg('$transientStorageId', $transientStoragePrefix)
->arg('$queryUserInfo', false)
- ->arg('$managementToken', $config['sdk']['management_token'])
+ ->arg('$managementToken', $sdkConfig['management_token'])
->arg('$managementTokenCache', $managementTokenCache)
->arg('$eventListenerProvider', $eventListenerProvider)
->arg('$backchannelLogoutCache', $backchannelLogoutCache)
- ->arg('$backchannelLogoutExpires', $config['sdk']['backchannel_logout_expires']);
+ ->arg('$backchannelLogoutExpires', $sdkConfig['backchannel_logout_expires']);
$container->services()
->set('auth0', Service::class)
diff --git a/src/Controllers/AuthenticationController.php b/src/Controllers/AuthenticationController.php
index 5db8cc0..e8b85ce 100644
--- a/src/Controllers/AuthenticationController.php
+++ b/src/Controllers/AuthenticationController.php
@@ -7,19 +7,30 @@
use Auth0\SDK\Auth0;
use Auth0\Symfony\Contracts\Controllers\AuthenticationControllerInterface;
use Auth0\Symfony\Security\Authenticator;
+use Psr\Container\ContainerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\Exception\{BadRequestException, ConflictingHeadersException, SuspiciousOperationException};
use Symfony\Component\HttpFoundation\{RedirectResponse, Request, Response};
use Symfony\Component\Routing\RouterInterface;
use Throwable;
+use function is_array;
+use function is_string;
+
final class AuthenticationController extends AbstractController implements AuthenticationControllerInterface
{
public function __construct(
private Authenticator $authenticator,
private RouterInterface $router,
+ protected ContainerInterface $container,
) {
}
+ /**
+ * @psalm-suppress InternalMethod
+ *
+ * @param Request $request
+ */
public function callback(Request $request): Response
{
$host = $request->getSchemeAndHttpHost();
@@ -31,7 +42,10 @@ public function callback(Request $request): Response
$code = $request->get('code');
$state = $request->get('state');
- if (null !== $code && null !== $state) {
+ $code = is_string($code) ? trim($code) : '';
+ $state = is_string($state) ? trim($state) : '';
+
+ if ('' !== $code && '' !== $state) {
$route = $this->getRedirectUrl('success');
try {
@@ -50,6 +64,10 @@ public function callback(Request $request): Response
}
}
+ /**
+ * @var string $redirect
+ */
+
return new RedirectResponse($redirect);
}
@@ -87,9 +105,14 @@ public function logout(Request $request): Response
private function getRedirectUrl(string $route): string
{
$routes = $this->authenticator->configuration['routes'] ?? [];
+
+ if (! is_array($routes)) {
+ $routes = [];
+ }
+
$route = $routes[$route] ?? null;
- if (null !== $route && '' !== $route) {
+ if (is_string($route) && '' !== $route) {
try {
return $this->router->generate($route);
} catch (Throwable) {
diff --git a/src/Controllers/BackchannelLogoutController.php b/src/Controllers/BackchannelLogoutController.php
index 5b25faf..0d54fca 100644
--- a/src/Controllers/BackchannelLogoutController.php
+++ b/src/Controllers/BackchannelLogoutController.php
@@ -7,6 +7,7 @@
use Auth0\SDK\Auth0;
use Auth0\Symfony\Contracts\Controllers\AuthenticationControllerInterface;
use Auth0\Symfony\Security\Authenticator;
+use Psr\Container\ContainerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\{RedirectResponse, Request, Response};
use Throwable;
@@ -17,9 +18,15 @@ final class BackchannelLogoutController extends AbstractController implements Au
{
public function __construct(
private Authenticator $authenticator,
+ protected ContainerInterface $container,
) {
}
+ /**
+ * @psalm-suppress InternalMethod
+ *
+ * @param Request $request
+ */
public function handle(Request $request): Response
{
if ('POST' !== $request->getMethod()) {
diff --git a/src/Models/Stateful/User.php b/src/Models/Stateful/User.php
index 30a1ca7..77439c8 100644
--- a/src/Models/Stateful/User.php
+++ b/src/Models/Stateful/User.php
@@ -8,5 +8,8 @@
class User extends \Auth0\Symfony\Models\User implements UserInterface
{
+ /**
+ * @var array
+ */
protected array $roleAuthenticatedUsing = ['ROLE_USING_SESSION'];
}
diff --git a/src/Models/Stateless/User.php b/src/Models/Stateless/User.php
index 8ffa829..ad29d1b 100644
--- a/src/Models/Stateless/User.php
+++ b/src/Models/Stateless/User.php
@@ -8,5 +8,8 @@
class User extends \Auth0\Symfony\Models\User implements UserInterface
{
+ /**
+ * @var array
+ */
protected array $roleAuthenticatedUsing = ['ROLE_USING_TOKEN'];
}
diff --git a/src/Models/User.php b/src/Models/User.php
index 6e7815c..920d631 100644
--- a/src/Models/User.php
+++ b/src/Models/User.php
@@ -10,14 +10,26 @@
use Symfony\Component\Security\Core\User\UserInterface as SymfonyUserInterface;
use function array_key_exists;
+use function is_array;
+use function is_bool;
+use function is_int;
use function is_string;
class User implements SymfonyUserInterface, UserInterface
{
+ /**
+ * @var array
+ */
protected array $roleAuthenticatedUsing = [];
+ /**
+ * @var array
+ */
protected array $roles = ['IS_AUTHENTICATED_FULLY', 'ROLE_USER'];
+ /**
+ * @param array $data
+ */
public function __construct(protected array $data)
{
}
@@ -26,9 +38,15 @@ public function eraseCredentials(): void
{
}
+ /**
+ * @param string $name
+ * @param mixed $default
+ *
+ * @return mixed
+ */
public function getAppMetadata(string $name, $default = null)
{
- if (! isset($this->data['app_metadata'])) {
+ if (! isset($this->data['app_metadata']) || ! is_array($this->data['app_metadata'])) {
return $default;
}
@@ -41,12 +59,24 @@ public function getAppMetadata(string $name, $default = null)
public function getClientId(): ?string
{
- return $this->data['clientID'] ?? null;
+ $clientId = $this->data['clientID'] ?? null;
+
+ if (! is_string($clientId)) {
+ return null;
+ }
+
+ return $clientId;
}
public function getCreatedAt(): ?DateTimeInterface
{
- return isset($this->data['created_at']) ? new DateTimeImmutable($this->data['created_at']) : null;
+ $createdAt = $this->data['created_at'] ?? null;
+
+ if (! is_string($createdAt)) {
+ return null;
+ }
+
+ return new DateTimeImmutable($createdAt);
}
public function getCustomData(string $key): mixed
@@ -56,74 +86,166 @@ public function getCustomData(string $key): mixed
public function getEmail(): ?string
{
- return $this->data['email'] ?? null;
+ $email = $this->data['email'] ?? null;
+
+ if (! is_string($email)) {
+ return null;
+ }
+
+ return $email;
}
public function getFamilyName(): ?string
{
- return $this->data['family_name'] ?? null;
+ $familyName = $this->data['family_name'] ?? null;
+
+ if (! is_string($familyName)) {
+ return null;
+ }
+
+ return $familyName;
}
public function getGivenName(): ?string
{
- return $this->data['given_name'] ?? null;
+ $givenName = $this->data['given_name'] ?? null;
+
+ if (! is_string($givenName)) {
+ return null;
+ }
+
+ return $givenName;
}
public function getId(): ?string
{
- return $this->data['user_id'] ?? null;
+ $id = $this->data['user_id'] ?? null;
+
+ if (! is_string($id)) {
+ return null;
+ }
+
+ return $id;
}
+ /**
+ * @return array
+ */
public function getIdentities(): array
{
- return $this->data['identities'] ?? [];
+ $identities = $this->data['identities'] ?? [];
+
+ if (! is_array($identities)) {
+ return [];
+ }
+
+ return $identities;
}
public function getLastIp(): ?string
{
- return $this->data['last_ip'] ?? null;
+ $lastIp = $this->data['last_ip'] ?? null;
+
+ if (! is_string($lastIp)) {
+ return null;
+ }
+
+ return $lastIp;
}
public function getLastLoginAt(): ?DateTimeInterface
{
- return isset($this->data['last_login']) ? new DateTimeImmutable($this->data['last_login']) : null;
+ $lastLoginAt = $this->data['last_login'] ?? null;
+
+ if (! is_string($lastLoginAt)) {
+ return null;
+ }
+
+ return new DateTimeImmutable($lastLoginAt);
}
public function getLastPasswordResetAt(): ?DateTimeInterface
{
- return isset($this->data['last_password_reset']) ? new DateTimeImmutable($this->data['last_password_reset']) : null;
+ $lastPasswordResetAt = $this->data['last_password_reset'];
+
+ if (! is_string($lastPasswordResetAt)) {
+ return null;
+ }
+
+ return new DateTimeImmutable($lastPasswordResetAt);
}
public function getLoginsCount(): int
{
- return $this->data['logins_count'] ?? 0;
+ $loginsCount = $this->data['logins_count'] ?? 0;
+
+ if (! is_int($loginsCount)) {
+ return 0;
+ }
+
+ return $loginsCount;
}
public function getMultifactor(): ?string
{
- return $this->data['multifactor'] ?? null;
+ $multifactor = $this->data['multifactor'] ?? null;
+
+ if (! is_string($multifactor)) {
+ return null;
+ }
+
+ return $multifactor;
}
public function getName(): ?string
{
- return $this->data['name'] ?? $this->data['nickname'] ?? null;
+ $name = $this->data['name'] ?? $this->data['nickname'] ?? null;
+
+ if (! is_string($name)) {
+ return null;
+ }
+
+ return $name;
}
public function getNickname(): ?string
{
- return $this->data['nickname'] ?? null;
+ $nickname = $this->data['nickname'] ?? null;
+
+ if (! is_string($nickname)) {
+ return null;
+ }
+
+ return $nickname;
}
public function getPhoneNumber(): ?string
{
- return $this->data['phone_number'] ?? null;
+ $phoneNumber = $this->data['phone_number'] ?? null;
+
+ if (! is_string($phoneNumber)) {
+ return null;
+ }
+
+ return $phoneNumber;
}
public function getPicture(): ?string
{
- return $this->data['picture'] ?? null;
+ $picture = $this->data['picture'] ?? null;
+
+ if (! is_string($picture)) {
+ return null;
+ }
+
+ return $picture;
}
+ /**
+ * @return array
+ *
+ * @psalm-suppress RedundantFunctionCall
+ */
public function getRoles(): array
{
$response = [];
@@ -139,32 +261,60 @@ public function getRoles(): array
$response[] = implode('_', explode(':', strtoupper($role)));
}
- foreach ($permissions as $permission) {
- $response[] = 'ROLE_' . implode('_', explode(':', strtoupper($permission)));
+ if (is_array($permissions)) {
+ foreach ($permissions as $permission) {
+ if (is_string($permission)) {
+ $response[] = 'ROLE_' . implode('_', explode(':', strtoupper($permission)));
+ }
+ }
}
- foreach ($scopes as $scope) {
- $response[] = 'ROLE_' . implode('_', explode(':', strtoupper($scope)));
+ if (is_array($scopes)) {
+ foreach ($scopes as $scope) {
+ if (is_string($scope)) {
+ $response[] = 'ROLE_' . implode('_', explode(':', strtoupper($scope)));
+ }
+ }
}
- $response[] = $this->roleAuthenticatedUsing;
+ foreach ($this->roleAuthenticatedUsing as $using) {
+ $response[] = $using;
+ }
return array_unique(array_values($response));
}
public function getUpdatedAt(): ?DateTimeImmutable
{
- return isset($this->data['updated_at']) ? new DateTimeImmutable($this->data['updated_at']) : null;
+ $updatedAt = $this->data['updated_at'] ?? null;
+
+ if (! is_string($updatedAt)) {
+ return null;
+ }
+
+ return new DateTimeImmutable($updatedAt);
}
public function getUserIdentifier(): string
{
- return $this->getId() ?? $this->data['sub'];
+ $userIdentifier = $this->getId() ?? $this->data['sub'];
+
+ if (! is_string($userIdentifier)) {
+ return '';
+ }
+
+ return $userIdentifier;
}
+ /**
+ * @param string $name
+ * @param mixed $default
+ *
+ * @return mixed
+ */
public function getUserMetadata(string $name, $default = null)
{
- if (! isset($this->data['user_metadata'])) {
+ if (! isset($this->data['user_metadata']) || ! is_array($this->data['user_metadata'])) {
return $default;
}
@@ -177,16 +327,28 @@ public function getUserMetadata(string $name, $default = null)
public function getUsername(): ?string
{
- return $this->data['username'] ?? null;
+ $username = $this->data['username'] ?? null;
+
+ if (! is_string($username)) {
+ return null;
+ }
+
+ return $username;
}
public function isBlocked(): ?bool
{
- return $this->data['blocked'] ?? null;
+ $blocked = $this->data['blocked'] ?? null;
+
+ if (! is_bool($blocked)) {
+ return null;
+ }
+
+ return $blocked;
}
public function isEmailVerified(): bool
{
- return filter_var($this->data['email_verified'], FILTER_VALIDATE_BOOLEAN) ?? false;
+ return filter_var($this->data['email_verified'], FILTER_VALIDATE_BOOLEAN);
}
}
diff --git a/src/Security/Authenticator.php b/src/Security/Authenticator.php
index f2c65c5..8768660 100644
--- a/src/Security/Authenticator.php
+++ b/src/Security/Authenticator.php
@@ -16,8 +16,17 @@
use Symfony\Component\Security\Http\Authenticator\Passport\{Passport, SelfValidatingPassport};
use Throwable;
+use function is_array;
+use function is_string;
+
final class Authenticator extends AbstractAuthenticator implements AuthenticatorInterface
{
+ /**
+ * @param array $configuration
+ * @param Service $service
+ * @param RouterInterface $router
+ * @param LoggerInterface $logger
+ */
public function __construct(
public array $configuration,
public Service $service,
@@ -34,7 +43,7 @@ public function authenticate(Request $request): Passport
throw new CustomUserMessageAuthenticationException('No Auth0 session was found.');
}
- $user = json_encode(['type' => 'stateful', 'data' => $session]);
+ $user = json_encode(['type' => 'stateful', 'data' => $session], JSON_THROW_ON_ERROR);
return new SelfValidatingPassport(new UserBadge($user));
}
@@ -45,9 +54,15 @@ public function onAuthenticationFailure(Request $request, SymfonyAuthenticationE
$request->getSession()->set('auth0:callback_redirect', $request->getUri());
}
- $route = $this->configuration['routes']['login'] ?? null;
+ $routes = $this->configuration['routes'] ?? [];
+
+ if (! is_array($routes)) {
+ $routes = [];
+ }
+
+ $route = $routes['login'] ?? null;
- if (null !== $route && '' !== $route) {
+ if (is_string($route) && '' !== $route) {
try {
return new RedirectResponse($this->router->generate($route));
} catch (Throwable) {
diff --git a/src/Security/Authorizer.php b/src/Security/Authorizer.php
index 704487c..e276cd5 100644
--- a/src/Security/Authorizer.php
+++ b/src/Security/Authorizer.php
@@ -14,8 +14,15 @@
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\{Passport, SelfValidatingPassport};
+use function is_string;
+
final class Authorizer extends AbstractAuthenticator implements AuthorizerInterface
{
+ /**
+ * @param array $configuration
+ * @param Service $service
+ * @param LoggerInterface $logger
+ */
public function __construct(
private array $configuration,
private Service $service,
@@ -23,16 +30,21 @@ public function __construct(
) {
}
+ /**
+ * @psalm-suppress InternalMethod
+ *
+ * @param Request $request
+ */
public function authenticate(Request $request): Passport
{
// Extract any available value from the authorization header
$param = $request->get('token', null);
- $header = trim($request->headers->get('Authorization', ''));
+ $header = trim($request->headers->get('Authorization', '') ?? '');
$token = $param ?? $header;
$usingHeader = null === $param;
// Ensure the 'authorization' header is present in the request
- if ('' === $token) {
+ if (! is_string($token) || '' === $token) {
throw new AuthenticationException('`Authorization` header not present.');
}
@@ -47,14 +59,17 @@ public function authenticate(Request $request): Passport
// Decode, validate and verify token.
$token = $this->getService()->getSdk()->decode(
token: $token,
- tokenType: \Auth0\SDK\Token::TYPE_TOKEN,
+ tokenType: \Auth0\SDK\Token::TYPE_ACCESS_TOKEN,
);
- $user = json_encode(['type' => 'stateless', 'data' => ['user' => $token->toArray()]]);
+ $user = json_encode(['type' => 'stateless', 'data' => ['user' => $token->toArray()]], JSON_THROW_ON_ERROR);
return new SelfValidatingPassport(new UserBadge($user));
}
+ /**
+ * @return array
+ */
public function getConfiguration(): array
{
return $this->configuration;
@@ -85,6 +100,11 @@ public function onAuthenticationSuccess(Request $request, TokenInterface $token,
return null;
}
+ /**
+ * @psalm-suppress InternalMethod
+ *
+ * @param Request $request
+ */
public function supports(Request $request): ?bool
{
if (null !== $request->get('token')) {
diff --git a/src/Security/UserProvider.php b/src/Security/UserProvider.php
index 5897413..d4bf4c5 100644
--- a/src/Security/UserProvider.php
+++ b/src/Security/UserProvider.php
@@ -15,6 +15,11 @@
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\User\{UserInterface as SymfonyUserInterface, UserProviderInterface as SymfonyUserProviderInterface};
+use function is_array;
+
+/**
+ * @template-implements SymfonyUserProviderInterface
+ */
final class UserProvider implements SymfonyUserProviderInterface, UserProviderInterface
{
public function __construct(
@@ -29,13 +34,21 @@ public function loadByUserModel(User $user): SymfonyUserInterface
public function loadUserByIdentifier(string $identifier): SymfonyUserInterface
{
- $identifier = json_decode($identifier, true);
+ $identifier = json_decode($identifier, true, 512, JSON_THROW_ON_ERROR);
+
+ if (! is_array($identifier)) {
+ throw new UnsupportedUserException();
+ }
+
+ $type = $identifier['type'] ?? null;
+ $data = $identifier['data'] ?? [];
+ $user = $data['user'] ?? null;
- if ('stateful' === $identifier['type']) {
- return new StatefulUser($identifier['data']['user']);
+ if ('stateful' === $type) {
+ return new StatefulUser($user);
}
- return new StatelessUser($identifier['data']['user']);
+ return new StatelessUser($user);
}
public function refreshUser(SymfonyUserInterface $user): SymfonyUserInterface
@@ -47,6 +60,9 @@ public function refreshUser(SymfonyUserInterface $user): SymfonyUserInterface
return $user;
}
+ /**
+ * @param string|UserInterface $class
+ */
public function supportsClass($class): bool
{
return $class instanceof UserInterface || is_subclass_of($class, UserInterface::class);
diff --git a/src/Service.php b/src/Service.php
index 76c70d4..f274309 100644
--- a/src/Service.php
+++ b/src/Service.php
@@ -14,7 +14,7 @@
final class Service implements ServiceInterface
{
- public const VERSION = '5.2.0';
+ public const VERSION = '5.2.1';
private ?Auth0 $sdk = null;
@@ -25,7 +25,7 @@ public function __construct(
) {
}
- public function getSdk(): ?Auth0
+ public function getSdk(): Auth0
{
if (! $this->sdk instanceof \Auth0\SDK\Auth0) {
$this->warmUp();
diff --git a/src/Stores/SessionStore.php b/src/Stores/SessionStore.php
index 734ee39..506cbb4 100644
--- a/src/Stores/SessionStore.php
+++ b/src/Stores/SessionStore.php
@@ -5,18 +5,37 @@
namespace Auth0\Symfony\Stores;
use Auth0\SDK\Contract\StoreInterface;
+use InvalidArgumentException;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpFoundation\{Request, RequestStack};
use Throwable;
+use function gettype;
+use function is_array;
+use function is_string;
+
final class SessionStore implements StoreInterface
{
+ /**
+ * @param string $namespace
+ * @param RequestStack $requestStack
+ * @param LoggerInterface $logger
+ *
+ * @psalm-suppress DocblockTypeContradiction
+ */
public function __construct(
private $namespace,
private RequestStack $requestStack,
private LoggerInterface $logger,
) {
+ if (! is_string($namespace)) {
+ $this->logger->error('SessionStore: $namespace must be a string, {type} given.', [
+ 'type' => gettype($namespace),
+ ]);
+
+ throw new InvalidArgumentException('$namespace must be a string.');
+ }
}
/**
@@ -41,7 +60,7 @@ public function delete(
): void {
$manifest = $this->session()?->get($this->namespace, []);
- if ([] === $manifest) {
+ if (! is_array($manifest) || [] === $manifest) {
return;
}
@@ -73,7 +92,7 @@ public function get(
) {
$manifest = $this->session()?->get($this->namespace, []);
- if ([] === $manifest || ! isset($manifest[$key])) {
+ if (! is_array($manifest) || [] === $manifest || ! isset($manifest[$key])) {
return $default;
}
@@ -100,6 +119,10 @@ public function set(
): void {
$manifest = $this->session()?->get($this->namespace, []);
+ if (! is_array($manifest)) {
+ $manifest = [];
+ }
+
$manifest[$key] = $value;
$this->session()?->set($this->namespace, $manifest);
@@ -115,13 +138,17 @@ private function session(
$request ??= $this->requestStack->getCurrentRequest();
$session = null;
+ if (! $request instanceof Request) {
+ return null;
+ }
+
try {
$session = $request->getSession();
} catch (Throwable) {
}
if ($session instanceof \Symfony\Component\HttpFoundation\Session\SessionInterface) {
- if ($session instanceof \Symfony\Component\HttpFoundation\Session\SessionInterface && ! $session->isStarted()) {
+ if (! $session->isStarted()) {
$session->start();
}