Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The algorithm with the alias "" is not supported #1223

Open
NeuralClone opened this issue May 22, 2024 · 4 comments · May be fixed by #1225
Open

The algorithm with the alias "" is not supported #1223

NeuralClone opened this issue May 22, 2024 · 4 comments · May be fixed by #1225

Comments

@NeuralClone
Copy link

NeuralClone commented May 22, 2024

Q A
Symfony Version 6.4.7
Bundle Version 3.0.0
PHP Version 8.3.7
Related issues/PRs #1209, #1214

I've been attempting to configure this bundle to use the Web-Token feature as outlined in the documentation and have run into some issues.

I have the following configuration:

lexik_jwt_authentication:
  secret_key: '%env(resolve:JWT_SECRET_KEY)%'
  public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
  pass_phrase: '%env(JWT_PASSPHRASE)%'
  api_platform:
    enabled: true
    check_path: /api/login_check
    username_path: username
    password_path: password
  encoder:
    service: lexik_jwt_authentication.encoder.web_token
  token_ttl: 3600
  allow_no_expiration: false
  clock_skew: 0
  user_id_claim: username
  token_extractors:
    authorization_header:
      enabled: true
      prefix: Bearer
      name: Authorization
    cookie:
      enabled: false
      name: BEARER
    query_parameter:
      enabled: false
      name: bearer
    split_cookie:
      enabled: false
      cookies: {  }
  remove_token_from_body_when_cookies_used: true
  set_cookies: {  }
  access_token_issuance:
    enabled: true
    signature:
      algorithm: RS256
      key: # some key
  access_token_verification:
    enabled: true
    signature:
      allowed_algorithms:
        - RS256
      keyset: # keyset
  blocklist_token:
    enabled: false
    cache: cache.app

If encryption isn't enabled, an InvalidArgumentException is always thrown by AccessTokenBuilder whenever a request for a token is made. For example, the following always fails when encryption isn't enabled:

curl -X POST http://localhost/api/login_check \
-H "Content-Type: application/json" \
-d '{"username":"someusername","password":"somepassword"}'

The problematic lines of code appear to be here:

$this->jwsBuilder = $jwsBuilderFactory->create([$signatureAlgorithm]);
if ($jweBuilderFactory !== null && $keyEncryptionAlgorithm !== null && $contentEncryptionAlgorithm !== null) {
$this->jweBuilder = $jweBuilderFactory->create([$keyEncryptionAlgorithm, $contentEncryptionAlgorithm]);
}

$keyEncryptionAlgorithm and $contentEncryptionAlgorithm aren't automatically set to null if they aren't defined and instead are set to empty strings. This always causes an exception to the thrown due to the encryption algorithm being set to an empty string. If I manually set both variables to null before the above lines of code, my configuration works and a token is correctly sent in the response.

The encryption related service arguments are replaced here, but only if encryption is enabled:

if ($config['access_token_issuance']['encryption']['enabled'] === true) {
$accessTokenBuilderDefinition
->replaceArgument(5, $config['access_token_issuance']['encryption']['key_encryption_algorithm'])
->replaceArgument(6, $config['access_token_issuance']['encryption']['content_encryption_algorithm'])
->replaceArgument(7, $config['access_token_issuance']['encryption']['key'])
;
}
}

Otherwise, they remain as empty strings, which doesn't appear to be the intention since it makes encryption mandatory.

I've attempted to directly set the encryption configuration options to null as a possible workaround, but that causes a different exception to be thrown that says they can't be null. That seems to directly contradict the service responsible for building tokens.

Am I missing an important configuration option/detail that addresses these issues or is this a bug? I've gone through the code and documentation in an attempt to find such an option and haven't had any luck. Is encryption supposed to be required when using the web-token feature?

@NeuralClone
Copy link
Author

NeuralClone commented Jun 4, 2024

I was finally able to get back to this today. After doing some additional debugging, the same problem exists in AccessTokenLoader:

$this->jwsLoader = $jwsLoaderFactory->create(['jws_compact'], $signatureAlgorithms, $jwsHeaderChecker);
if ($jweLoaderFactory !== null && $keyEncryptionAlgorithms !== null && $contentEncryptionAlgorithms !== null && $jweHeaderChecker !== null) {
$this->jweLoader = $jweLoaderFactory->create(['jwe_compact'], array_merge($keyEncryptionAlgorithms, $contentEncryptionAlgorithms), null, null, $jweHeaderChecker);
$this->continueOnDecryptionFailure = $continueOnDecryptionFailure;
}

Likewise, the service arguments are set here:

if ($config['access_token_verification']['encryption']['enabled'] === true) {
$accessTokenLoaderDefinition
->replaceArgument(8, $config['access_token_verification']['encryption']['continue_on_decryption_failure'])
->replaceArgument(9, $config['access_token_verification']['encryption']['header_checkers'])
->replaceArgument(10, $config['access_token_verification']['encryption']['allowed_key_encryption_algorithms'])
->replaceArgument(11, $config['access_token_verification']['encryption']['allowed_content_encryption_algorithms'])
->replaceArgument(12, $config['access_token_verification']['encryption']['keyset'])
;
}

The service is expecting the encryption algorithms and associated keysets to be null but are empty strings instead. Therefore, verification always fails.

The easiest solution in both cases would probably be to make sure the appropriate arguments are set to null if they aren't set in the configuration.

Workaround

I've been able to fix both of these issues in my Kernel class by implementing CompilerPassInterface and directly overriding the default values as described in the Symfony documentation: How to Work with Compiler Passes.

// src/Kernel.php
namespace App;

use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;

class Kernel extends BaseKernel implements CompilerPassInterface
{
    use MicroKernelTrait;
    
    // ...

    public function process(ContainerBuilder $container): void
    {
        $accessTokenBuilderService = 'lexik_jwt_authentication.access_token_builder';
        $accessTokenLoaderService = 'lexik_jwt_authentication.access_token_loader';

        if ($container->hasDefinition($accessTokenBuilderService)) {
            $container->getDefinition($accessTokenBuilderService)
                ->replaceArgument(5, null)
                ->replaceArgument(6, null)
                ->replaceArgument(7, null);
        }

        if ($container->hasDefinition($accessTokenLoaderService)) {
            $container->getDefinition($accessTokenLoaderService)
                ->replaceArgument(9, null)
                ->replaceArgument(10, null)
                ->replaceArgument(11, null)
                ->replaceArgument(12, null);
        }
    }

    // ...
}

This doesn't solve the core problem, but it's a reasonable and easy to implement workaround until it is fixed.

@chalasr
Copy link
Collaborator

chalasr commented Jun 4, 2024

Thanks for the detailed report and workaround!
I'm going to look into this asap though you're welcome if you feel like trying to fix the root cause :)

@NeuralClone
Copy link
Author

NeuralClone commented Jun 6, 2024

You're welcome! I've created a pull request that addresses the root cause. Setting default values for the problematic arguments in the service configurations and changing the logic to look for empty arrays instead of null in AccessTokenLoader are the only changes that were needed.

This fix was originally suggested in #1209, but it wasn't fully implemented in #1214. Empty strings were set as default values and the logic wasn't updated in AccessTokenLoader to address the empty arrays.

@NeuralClone
Copy link
Author

Are there any updates on this? Is there anything you'd like changed/reworked in the pull request I created?

I'm using the workaround I described above in projects that use this bundle and that's continued to work well. So, there's absolutely no rush. But it'd be nice to not have to use a compiler pass to fix the configuration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants