KnpUOAuth2ClientBundle and Google PHP API - php

I have a Symfony 5 Application that is authenticating users by their Google account using KnpUOAuth2ClientBundle.
I now want to integrate the Google Drive API so the user can upload files to his Drive.
https://developers.google.com/drive/api/v3/quickstart/php
is giving some documentation about that.
What I am wondering: If the user is already authenticated (I have a valid user access token), do I have to run through the whole authentication process again or can I use the authentication token that was generated by KnpUOAuth2ClientBundle? And how do I get this authentication token as an object? When I am trying to get it via $client->getAccessToken() I get the error "Invalid State".

The answer is: Yes, you can use the existing login token to authenticate with further services like Google Drive API. You just need the existing login token as a string.
Be aware that you might have to add additional scopes to the login like this:
$clientRegistry
->getClient('google_main') // key used in config/packages/knpu_oauth2_client.yaml
->redirect([
'email',
'profile', // the scopes you want to access
Google_Service_Drive::DRIVE_FILE,
], []);
Then in your Google Drive Service you can initiate the Client like this:
/**
* Returns an authorized API client.
* #return Google_Client the authorized client object
*/
protected function initClient()
{
$user = $this->security->getUser();
$googleAccessToken = $user->getGoogleAccessToken();
$this->client->setApplicationName('MY APPLICATION');
$this->client->setScopes(Google_Service_Drive::DRIVE_METADATA_READONLY);
$this->client->setAccessType('offline');
$this->client->setPrompt('select_account consent');
$googleToken = [
'access_token' => $googleAccessToken,
'id_token' => $googleAccessToken,
// user is having a valid token from the login already
'created' => time() - 3600,
'expires_in' => 3600,
];
$this->client->setAccessToken($googleToken);
return $this->client;
}

Related

Google Cloud Platform API Creating Service Accounts by a Service account (PHP)

I'm trying to create a service account on a project using a service account but I can't get it to work. Right now I'm just simply trying to list all service accounts from a project using a service account who was permission but it returns a 401 error. Is this even possible to do with a service account?
$sa = new ServiceAccountCredentials([
'iam.serviceAccounts.list'
], base_path() . '/credentials.json');
$middleware = new AuthTokenMiddleware($sa);
$stack = HandlerStack::create();
$stack->push($middleware);
// create the HTTP client
$client = new Client([
'handler' => $stack,
'base_uri' => 'https://iam.googleapis.com',
'auth' => 'google_auth' // authorize all requests
]);
$response = $client->get('v1/projects/project-id-1/serviceAccounts');
var_dump((String) $response->getBody());
Response:
{
"error": {
"code": 401,
"message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED"
}
}
Edit:
My packages:
"google/auth": "^1.19",
"guzzlehttp/guzzle": "^7.0.1"
The second line of your code 'iam.serviceAccounts.list' is incorrect. You are specifying an IAM permission. The method requires a scope.
Refer to the documentation for this method.
$sa = new ServiceAccountCredentials([
'https://www.googleapis.com/auth/cloud-platform'
], base_path() . '/credentials.json');
With your second parameters, when the method is called, a lookup of the requested scope is performed. Since you did not specify a valid scope, the end result is you have a credential with no permissions. That causes the error message:
Request had invalid authentication credentials. Expected OAuth 2 access token ...
Do not control permissions via scopes when using service accounts. Use IAM roles to define which permissions a service account has. Then use the cloud-platorm scope.
I wrote an article on Google Cloud credentials and PHP that might help with details:
Google Cloud Application Default Credentials – PHP

Twinfield API OAuth2.0 getaccessToken php-twinfield/twinfield

I am currently trying to setup the Twinfield API, it should be pretty straight forward when using the php-twinfield/twinfield library. But there is one thing I don't fully understand.
Here is my code:
$provider = new OAuthProvider([
'clientId' => 'someClientId',
'clientSecret' => 'someClientSecret',
'redirectUri' => 'https://example.org/'
]);
$accessToken = $provider->getAccessToken("authorization_code", ["code" => ...]);
$refreshToken = $accessToken->getRefreshToken();
$office = \PhpTwinfield\Office::fromCode("someOfficeCode");
$connection = new \PhpTwinfield\Secure\OpenIdConnectAuthentication($provider,
$refreshToken, $office);
The $accessToken require something on the dots, some sort of code. I am not sure what that should be...
I hope someone can help me out. Thanks already!
I am still stuck with oauth2 setup... the provider seems to have all the information it needs to have. It returns a code which is needed to retrieve an accessToken. But, trying to get one using the following code:
$accessToken = $provider->getAccessToken('authorization_code',
['code' => $_GET['code']]);
This will return 'invalid_grant'.
I have tried to reset my clientSecret... but that did not help.
I hope somebody can help me any further.
To access the Twinfield API the users must be authenticated. You can either do this by specifying a username and password or using OAuth2. When using OAuth2 you delegate the authentication to a so called OAuth Provider. After the user authenticated, the provider will redirect the user's browser to an endpoint (redirectUri) at your application. That request, that your application receives, has a GET parameter called code. Your app will then exchange the code for a token using its clientId and clientSecret and HTTP POST. Which means that your application must be registered at the OAuth2 provider so that the provider (e.g. github, facebook, google, ...) can validate the client credentials and return a token. And you will have to configure your provider variable to point to the OAuth provider that you connect with.
$provider = new OAuthProvider([
'clientId' => 'XXXXXX', // The client ID assigned to you by the provider
'clientSecret' => 'XXXXXX', // The client password assigned to you by the provider
'redirectUri' => 'https://example.com/your-redirect-url/',
'urlAuthorize' => 'https://login.provider.com/authorize', //where the user's browser should be redirected to for triggering the authentication
'urlAccessToken' => 'https://login.provider.com/token', //where to exchange the code for a token
'urlResourceOwnerDetails' => 'https://login.provider.com/resource' //where to get more details about a user
]);
// If we don't have an authorization code then get one
if (!isset($_GET['code'])) {
// Fetch the authorization URL from the provider
// Redirect the user to the authorization URL.
}
Twinfield makes use of league/oauth2-client library for implementing OAuth. Therefore, refer to https://oauth2-client.thephpleague.com/usage/ for the details on how to setup an OAuth client in the twinfield library. league/oauth2-client supports some providers out of the box and allows third-party providers. Your provider may be in any of the lists. If not, refer to the documentation of your provider to get the right URLs.

Laravel API Registration

I am developing a laravel back-end to work with Vue JS, and trying to implement user registration. I have downloaded passport to authenticate users, but I am having trouble understanding exactly how a new user registers for the website.
From the research I have done it seems like you would want to make a route for the registration that can be accessed without using laravel passport, and then once the user is created grant it a token.
Once the user is registered Would I use a personal grant token, Implicit, or use passports CreateFreshApiToken middleware.
Should I put all my routes in web or the api route file?
You may create a route in your api where anybody can register a new user , so you do not use any auth middlewares, like for example:
Route::post('users', 'AuthController#register'); // Signup
and in your controller the related method:
/**
* API Register
*
* #param Request $request
* #return \Illuminate\Http\JsonResponse
*/
public function register(Request $request)
{
$rules = [
'name' => 'unique:users|required',
'email' => 'unique:users|required',
'password' => 'required',
];
$input = $request->only('name', 'email','password');
$validator = Validator::make($input, $rules);
if ($validator->fails()) {
return response()->json(['success' => false, 'error' => $validator->messages()]);
}
$name = $request->name;
$email = $request->email;
$password = $request->password;
$user = User::create(['name' => $name, 'email' => $email, 'password' => Hash::make($password)]);
}
from this point the type of passport OAuth2 authorization code flow you choose will influence your implementation on the consumer app.
You may go for the classical OAuth2 flow where basically you have this steps:
Register the consumer application to the OAuth2 server and obtain the Client Id and Secret
The consumer application request an authorization code to the OAuth2 server using the Client Id and Secret
Once obtained the authorization code the consumer application can now request an access token to the OAuth2 server
The consumer application can now have access to the Api using in every request to the server its access token that it is sent in the header request.
Obviously each step above is an HTTP request and how you do it depends on the technology you use in the consumer application.
For example in php, you can use Guzzle and send an access token request like this:
$http = new \GuzzleHttp\Client;
$response = $http->post('http://yourserver.com/oauth/token', [
'form_params' => [
'grant_type' => 'authorization_code',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'redirect_uri' => 'http://example.com/callback',
'code' => $request->code,
],
]);
On the other side you may want to use a simpler way to retrieve access tokens, avoiding all the authorization code request etc. using Laravel passport Personal Access Tokens. In this way you can issue access tokens for the user simply doing:
$user = App\User::find(1);
// Creating a token without scopes...
$token = $user->createToken('Token Name')->accessToken;
I encourage you to take a look to the Passport Laravel documentation and check all the possibilities offered.

Social authentication in laravel

I am trying to authenticate using social authentication in laravel. I have this code in services.php
'facebook' => [
'client_id' => 'your-facebook-id',
'client_secret' => 'your-facebook-app-secret',
'redirect' => 'http://your-callback-url',
],
And whenever i try to login the facebook shows:
Invalid App ID: your-facebook-id
What actually are client_id, client_secret, and redirect here?
I have these methods for redirecting the user to the OAuth provider, and another for receiving the callback from the provider after authentication.
public function redirectToProvider()
{
return Socialite::driver('facebook')->redirect();
}
public function handleProviderCallback()
{
$user = Socialite::driver('facebook')->user();
dd($user->getEmail);
}
You get the client_id and the client_secret from Facebook when you create an "app" in their system (In this context an app isn't what you might think initially).
https://developers.facebook.com/
Once created, your client_id will be what they call "App ID" and your client_secret will be what they call "App Secret". You need to click the "Show" button and re-authenticate to get hold of your secret.
Your redirect is where you want Facebook to redirect you after the user has authorised your app. Generally this redirect should be where you're actually handling the login process, which in your case is the URL/route that passes control to your handleProviderCallback() method.

Getting Google Refresh Token with Laravel Socialite

Using Laravel Socailite to connect to the Google API and I am getting back a connection fine, however this access doesn't return a refresh token so my connection is timing out.
$scopes = [
'https://www.googleapis.com/auth/webmasters',
'https://www.googleapis.com/auth/webmasters.readonly',
'https://www.googleapis.com/auth/analytics.readonly',
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/userinfo.email',
];
$parameters = ['access_type' => 'offline'];
return Socialite::driver('google')->scopes($scopes)->with($parameters)->redirect();
How do I get the refresh token back?
When you redirect your users to Google, set access_type to offline with the with() method when redirecting, like this:
return Socialite::driver('google')
->scopes() // For any extra scopes you need, see https://developers.google.com/identity/protocols/googlescopes for a full list; alternatively use constants shipped with Google's PHP Client Library
->with(["access_type" => "offline", "prompt" => "consent select_account"])
->redirect();

Categories