Authorizing API requests using FOSOAuthBundle in UnitTests - symfony

I try to authorize a test user in my UnitTest cases. For this I create for the tests the following helper function (that is not very handy, but I can make this better afterwards):
public function generateOAuthLoginData(EntityManager $em, Client $client) {
$apiclient = new \OAuthBundle\Entity\Client();
$apiclient->setRandomId('randomid');
$apiclient->setSecret('secret');
$apiclient->setAllowedGrantTypes(['password', 'refresh_token']);
$em->persist($apiclient);
$user = new \AppBundle\Entity\User();
$user->setEmail('user#test.de');
$user->setUsername('user');
$user->setPlainPassword('password');
$user->setFirstname('User');
$user->setLastname('Test');
$user->addRole('ROLE_ADMIN');
$em->persist($user);
$em->flush();
$crawler = $client->request('GET', '/oauth/v2/token?client_id=1_randomid&client_secret=secret&grant_type=password&username=user#test.de&password=password');
$access_token = json_decode($client->getResponse()->getContent())->access_token;
return ['ACCEPT' => 'application/json', 'AUTHORIZATION' => 'Bearer '.$access_token];
}
I get a access token (checked it in the code and in the database) and now I try to make some API requests by executing
$crawler = self::$client->request('GET', '/api/users', [], [], self::$authorisationHeaders);
$this->assertEquals(200, self::$client->getResponse()->getStatusCode());
in the test class.
I tried with RESTer (a Firefox plugin to make custom requests) and there it works. But in the tests I get a 401 error. Here is the error out of the log file:
[2017-04-18 13:45:32] security.INFO: An AuthenticationException was thrown; redirecting to authentication entry point. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\AuthenticationCredentialsNotFoundException(code: 0): A Token was not found in the TokenStorage. at /var/www/testproject/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/AccessListener.php:53)"} []
[2017-04-18 13:45:32] security.DEBUG: Calling Authentication entry point. [] []
What's my fault? And why does it work in RESTer and not in the UnitTests?

Did you tried to use HTTP_Authorization instead of AUTHORIZATION as an array key?

Related

What is the correct format and sequence to acquire the correct token for LinkedIn API V2?

Our LinkedIn APP no longer works with the evolution of V2. I have tried a couple of times and failed to create the correct token. I am seeking help to create the correct authorization link to get the token. Error from the App is currently "Empty oauth2 access token"
I created a new LinkedIn app to replace our old one. I have tried to follow the instructions from LinkedIn and Microsoft but my efforts have produced the following error
My most recent attempt was:
https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=78xaqf0ereiisy&redirect_uri=https://www.gachina.com&state=gachina&scope=r_emailaddress r_liteprofile w_member_social
I received: https://www.gachina.com/?code=AQS65Njp1F9-L-mKSrAJKZeQ-ij2OX7wboTc30-hrfQIwwJ0yfWd4FBqxLl-ZXHmL5HurKud4t9WcGeHB62EfPNcy3ddoqT1LztUHhR59iL-Q8f9WLrX03d9e3OCTmY-3vR8a_4ENeIN0GFpeLy7DKRDmuUNcQ82UwScqhApdtwzEBw-_Y0duYG87Lc1KQ&state=gachina
then I used the format shown here:
https://learn.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow
https://www.linkedin.com/oauth/v2/accessToken?grant_type=authorization_code&code={authorization_code_from_step2_response}&redirect_uri=hhttps%3A%2F%2Fdev.example.com%2Fauth%2Flinkedin%2Fcallback&client_id={your_client_id}&client_secret={your_client_secret}
with
https://www.linkedin.com/oauth/v2/accessToken?grant_type=authorization_code&code=AQS65Njp1F9-L-mKSrAJKZeQ-ij2OX7wboTc30-hrfQIwwJ0yfWd4FBqxLl-ZXHmL5HurKud4t9WcGeHB62EfPNcy3ddoqT1LztUHhR59iL-Q8f9WLrX03d9e3OCTmY-3vR8a_4ENeIN0GFpeLy7DKRDmuUNcQ82UwScqhApdtwzEBw-_Y0duYG87Lc1KQ&redirect_uri=https://www.gachina.com/auth/Linkedin/callback&client_id=78xaqf0ereiisy&client_secret={client_secret}
but I receive the following upon submitting the above link with our {client secret} in place
{"error":"invalid_redirect_uri","error_description":"Unable to retrieve access token: appid/redirect uri/code verifier does not match authorization code. Or authorization code expired. Or external member binding exists"}
I am doing all of this within minutes. So, I do not believe there is an expiration of code.
Can you help identify the error of steps or code to receive a Token?
This will return the access token
$params = array('grant_type' => 'authorization_code',
'client_id' => $this->api_key,
'client_secret' => $this->api_secret,
'code' => $_GET['code'],
'redirect_uri' => base_url().$this->redirect,
);
// Access Token request
$url = 'https://www.linkedin.com/oauth/v2/accessToken?' . http_build_query($params);
$data_len = strlen(http_build_query($params));
// Tell streams to make a POST request
$context = stream_context_create(
array('http' =>
array('method' => 'POST','header'=> 'Content-Length: 0'
)
)
);
// Retrieve access token information
$response = file_get_contents($url, false, $context);
$token = json_decode($response);
return $token->access_token;

UsernamePasswordToken NULL after one request

I created UnitTests for my Symfony app with the REST and OAuthBundle. To test the API behind the firewall, I create in my setUp method a UsernamePasswordToken by
$token = new UsernamePasswordToken($user, null, 'default', array('ROLE_USER'));
Now I set the token by
self::$client->getContainer()->get('security.token_storage')->setToken($token);
Interestingly this token is only for one request in the storage. The first request with the first assertion succeeds, the second fails because of an 401 error. I checked the storage afterwards and the getToken() method returns NULL. If I set the token once more before the next request, this request succeeds also.
This is a sample request and the assertion:
$crawler = self::$client->request('GET', '/api/users');
$this->assertEquals(200, self::$client->getResponse()->getStatusCode());
So, I can set the token before each single request to solve the problem, but this would very annoying in all my tests. Why is the token after one "use" gone and how can I set a "lifetime" or something else?
I think the problem is that each request the kernel and with it the container will load from the cache again where it does not contain your token. You have to persist your token in the session for it to stay permanently. How to do this is described in the documentation Testing HTTP Authentication
protected function login()
{
$session = $this->client->getContainer()->get('session');
// the firewall context defaults to the firewall name
$firewallContext = 'secured_area';
$token = new UsernamePasswordToken('admin', null, $firewallContext, array('ROLE_ADMIN'));
$session->set('_security_'.$firewallContext, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}
This should work over multiple requests and you can set it per test-method if you still want some tests not to be logged in.

Functional test of form Symfony3 mocked session

I'm writing functional tests for my symfony3 application and I have a problem that when I'm trying to test sending forms, by crawling the submit button.
Method what I want to test:
$personalDataForm->handleRequest($request);
if ($personalDataForm->isValid()) {
return $this->handlePersonalForm($personalDataForm, $request);
}
return $this->render('members/personal-form.html.twig', [
'personalDataForm' => $personalDataForm->createView(),
]);
Functional test:
$client = static::createClient();
$client->getCookieJar()->set($this->cookie);
$client->followRedirects();
$container = $client->getContainer();
$container->set('session', $this->session);
$crawler = $client->request('GET', '/');
$form = $crawler->selectButton('Save')->form();
$client->submit($form);
Declaration of $this->session:
$this->session = new Session(new MockArraySessionStorage(), new AttributeBag(), new FlashBag())
The crawler is correctly sending a request, but I have an error Failed to start the session because headers have already been sent by "phar:///usr/local/bin/phpunit/phpunit/Util/Printer.php" at line 134.
When I'm looking at test result I see that my test didn't pass the $personalDataForm->isValid() condition.
I think the problem is that after submitting form, application is using Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage to store a session instead of mocked session. How can I pass mocked session when I'm submitting form?

Symfony2 + FosOauth: token get, but requests are anonymous

I have installed and configurated FosOauthBundle but I have this problem: I can get Token And Refresh token with this line of code:
$ http POST http://localhost:8888/app_dev.php/oauth/v2/token grant_type=password client_id=1_3bcbxd9e24g0gk4swg0kwgcwg4o8k8g4g888kwc44gcc0gwwk4 client_secret=4ok2x70rlfokc8g0wws8c8kwcokw80k44sg48goc0ok4w0so0k username=MYEMAIL password=MYPASS
If I pass a wrong MYEMAIL or a wrong MYPASS symfony reply me with an error (and this is correct). Users are manged via FosUserBundle.
Now, How can I use token to say symfony that I'm a registered user ?
I have created this "api" in my controller:
public function getDemosAction()
{
$user = $this->get('security.token_storage')->getToken()->getUser();
$view = $this->view($user);
return $this->handleView($view);
}
and I call it via this code:
$curl -H "Authorization: Bearer NGEwMGIxMzJkZmU5Yjc3YmM2ZjViNmE0YWFhYTEwOTg1MjI5NzIyNDkwNmFhYTUzMTRkZTk3MzEyNjA4OWY0Ng" http://localhost:8888/app_dev.php/api/demos
but it return me ALWAYS "anon.".
Where is my error ?
The problem was the order of firewall directives in security.yml. I have found the solution via this other question: Symfony2 OAuth keeps giving me a login page when a token is provided

Why I am getting no authentication token error?

I have the folowing snippet of code to check use role however it returns me with a
The security context contains no authentication token. One possible reason may be that there is no firewall configured for this URL.
public function loginAction(){
$request = $this->getRequest();
$session = $request->getSession();
var_dump($this->get("security.context")->isGranted('ROLE_ADMIN'));
$response = new Response();
$response -> setContent("login facebook");
$response->send();
return array('name'=>'login facebook');
}
Why I am getting this error and how do I fix this ?
The exception message is pretty clear: One possible reason may be that there is no firewall configured for this URL. To check user role, you need a token which contains a user (anonymous or not). By default, if you don't configure a authentication provider for a given path, you won't get any security context.
The solution here is to add an authentication provider.

Resources