trikoder / oauth2-bundle

Symfony bundle which provides OAuth 2.0 authorization/resource server capabilities.
https://www.trikoder.net/
MIT License
249 stars 114 forks source link

[Question/Help] How to get an accessToken into my tests? #243

Open fkizewski opened 3 years ago

fkizewski commented 3 years ago

Hi all,

I'm building an API with Symfony 5, with ApiPlatform and i've integrated your awesome bundle.

I would like to get an accessToken to be able to launch my test with a user. I'm not sure I'm doing it right, because when i'm using $client = static::createClient(), i've got the error unsupported_grant_type, but when i'm using $client = HttpClient::create(), i've receive that token, but of course, I would like to use the first method to get the client (to use the PhpUnit build into PhpStorm and not the directly command to launch test via phpunit, it's a better UI to make and try tests). I've try to fix it by adding the necessary Content-Type:application/x-www-form-urlencoded, without success. I've try to make the same things into your tests or thephpleague/oauth2-server without success.

In postman when i calling my API with the same information all is fine : I've an accessToken.

Can you please put me on the right way. Thanks in advance, Fabrice



namespace App\Tests;

use ApiPlatform\Core\Bridge\Symfony\Bundle\Test\ApiTestCase;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpFoundation\Request;

class AbstractTestClass extends ApiTestCase
{
    public function testGetUserAccessToken() {
        $client = HttpClient::create(); // Success
        // $client = static::createClient(); // Error => unsupported_grant_type

        $response = $client->request(
            Request::METHOD_POST,
            'oauth/v2/token',
            [
                'body' => [
                    'grant_type' => 'password',
                    'username' => 'email',
                    'password' => 'password',
                    'scope' => '',
                    'client_id' => 'CLIENT_ID',
                    'client_secret' => 'CLIENT_SECRET',
                ]
            ]
        );
        echo $response->getStatusCode(),"\r\n";
        echo $response->getContent(false),"\r\n";
    }
}
cirdan commented 3 years ago

Hi, I'm struggling with the same issue. The bundle works seamlessly, except when we try to setup even a simple test requesting a token.

@fkizewski , did you manage to have green tests ?

fkizewski commented 3 years ago

Hi @cirdan,

Here please find how i've done, because there is no official possibility/doc... When my test is launched, my fixture automatically create :

I've created my own test class (in my case, i using symfony): class MyApiTestCase extends KernelTestCase And inside, i have a magic function (see below).

If you have any further questions please do not hesitate ;) Fabrice



namespace App\Tests;

use DateTimeImmutable;
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\Key\LocalFileReference;
use Lcobucci\JWT\Signer\Rsa\Sha256;
use League\OAuth2\Server\CryptKey;
use Trikoder\Bundle\OAuth2Bundle\Model\AccessToken;

protected function getAccessToken()
{
    static::bootKernel();
    $this->em = static::$container->get('doctrine')->getManager();

    // Get the first AccessToken available into the database
    /** @var AccessToken $accessToken */
    $accessToken = $this->em->getRepository(AccessToken::class)->findAll()[0];

    $privateKey = new CryptKey($_ENV['PRIVATE_KEY'], $_ENV['PRIVATE_KEY_PASSPHRASE'], false);

    // inspired by https://github.com/thephpleague/oauth2-server/blob/master/src/Entities/Traits/AccessTokenTrait.php
    $jwtConfiguration = Configuration::forAsymmetricSigner(
        new Sha256(),
        LocalFileReference::file($privateKey->getKeyPath(), $privateKey->getPassPhrase() ?? ''),
        InMemory::plainText('')
    );

    return $jwtConfiguration->builder()
        ->permittedFor($accessToken->getClient()->getIdentifier())
        ->identifiedBy($accessToken->getIdentifier())
        ->issuedAt(new DateTimeImmutable())
        ->canOnlyBeUsedAfter(new DateTimeImmutable())
        ->expiresAt(DateTimeImmutable::createFromInterface($accessToken->getExpiry()))
        ->relatedTo((string) $accessToken->getUserIdentifier())
        ->withClaim('scopes', $accessToken->getScopes())
        ->getToken($jwtConfiguration->signer(), $jwtConfiguration->signingKey())->toString();
}
anitaeisenhaber commented 3 years ago

If someone else runs into this problem: @fkizewski payload in the request is incorrect. It should be:

$response = $client->request(
            Request::METHOD_POST,
            'oauth/v2/token',
            [
                  'grant_type' => 'password',
                  'username' => 'email',
                  'password' => 'password',
                  'scope' => '',
                  'client_id' => 'CLIENT_ID',
                  'client_secret' => 'CLIENT_SECRET',
            ]
        );
iosdevlicard commented 3 years ago
$options['extra']['parameters'] = [
            'client_id'=>'CLIENT_ID',
            'client_secret' => 'CLIENT_SECRET',
            'grant_type' => 'password',
            'username' => 'email',
            'password' => 'pass',
        ];
        $client = static::createClient();
        $response = $client->request('POST', '/oauth/v2/token', $options);
smilesrg commented 3 years ago

I had to use fixtures loader to make a test client id and secret:

class OAuth2ClientFixtures extends Fixture
{
    public function load(ObjectManager $manager): void
    {
        $oAuth2Client = new Client('OAuth2ClientId', 'OAuth2Secret');
        $manager->persist($oAuth2Client);
        $manager->flush();
    }
}

And then make the request:

        $this->client->request('POST', '/oauth2/token', [
            'client_id' => 'OAuth2ClientId',
            'client_secret' => 'OAuth2Secret',
            'grant_type' => 'client_credentials',
        ]);