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

Feature oauth v2 authorization code #14

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 57 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,67 @@ Now find the `aliases` array in the same config file and add the following Facad

## Config

Before you can use the service provider you have configure it. You can create and App with API access keys here: [GotoWebinar Developer portal](https://goto-developer.logmeininc.com). Look for the `My Apps` menu.
If you are migrating from an older deprecated version of the OAuth API using Direct Login, read the following [guide to migrate](https://developer.goto.com/guides/References/05_Direct-Login_migration/) your setup using the default `grand_type=authorization_code` auhtorization method.

Note that you need to have an active or trial account for the API to function properly. Just dev credentials alone might not work since those tokens are sometimes restricted or expire.
### Authorization

The provider currently only support [OAuth2](https://goto-developer.logmeininc.com/how-get-access-token-and-organizer-key) authentication. Since this is used for backend integration and not clients like for examples mobile applications, etc. the initial authentication is done via Goto's [Direct Login](https://goto-developer.logmeininc.com/how-use-direct-login).
In order to obtain an access-token, an authorization code needs to be requested first. This request **has** to be done **manually**, e.g. by Using a browser. To the contrary of using Direct Login, it is not possible to automate this process using a `username` and `password`.

The package's configuration requires at a minimum the following environment values are required in your `.env` file.
Read the following page [How to create an OAuth client](https://developer.goto.com/guides/HowTos/02_HOW_createClient/) to receive your `Client ID`, also known as the `CONSUMER KEY`. Please do make sure to use the same redirect URL in your environment settings as specified in the created OAuth client. The redirect URL does **not** have to exist, it's only required to retrieve your authorization `&code=` parameter.

Once received, fill the following environment values that need to be configured in your `.env` file.

```
GOTO_CONSUMER_KEY=Oa0fdvd82FdXcLrsts3EQYdsuGhdscV41
GOTO_CONSUMER_SECRET=8mbIGtkfdfhjksad68
GOTO_REDIRECT_URI=https://example.com/redirect
```

Generate an authorization link executing the following command

```
php artisan goto:authorize-link
```

If you wish to add a state parameter, add the option `--state=your-state`.

Open the link by clicking or copy/paste in your browser. Login within your credentials and receive the `code` parameter. If your redirect URL is called `https://example.com` the redirect URL will be something like:

```
https://example.com?code=a-very-long-code
```

Copy this code and set this in your `.env` file:

```
GOTO_AUTHORIZATION_CODE=this-very-long-code
```

To retreive an access token, execute the following command with the `--flush` any cached config and stored access-token. Or if locally or in development mode, use the following route `/_goto/authenticate`.


```
php artisan goto:access-token --flush
```

In order to view the currently active access-token/refresh-token, simply call the same command without `--flush`.

```
php artisan goto:access-token
```

### Authorizing using the deprecated Direct Login method

If your account is still able to use the [Direct Login](https://goto-developer.logmeininc.com/how-use-direct-login) method (using `grand_type=password`, you can create an App with API access keys here: [GotoWebinar Developer portal](https://goto-developer.logmeininc.com). Look for the `My Apps` menu.

Note that you need to have an active or trial account for the API to function properly. Just dev credentials alone might not work since those tokens are sometimes restricted or expired.

The provider currently only support [OAuth2](https://goto-developer.logmeininc.com/how-get-access-token-and-organizer-key) authentication. Since this is used for backend integration and not clients like for examples mobile applications, etc. the initial authentication is done via Goto's [Direct Login](https://goto-developer.logmeininc.com/how-use-direct-login).

The package's configuration for legacy direct login requires at a minimum the following required environment values in your `.env` file.

```
GOTO_LEGACY=true
GOTO_CONSUMER_KEY=Oa0fdvd82FdXcLrsts3EQYdsuGhdscV41
GOTO_CONSUMER_SECRET=8mbIGtkfdfhjksad68
[email protected]
Expand Down Expand Up @@ -535,4 +587,4 @@ Your contribution or bug fixes are welcome!

Enjoy!

Slakkie
Slakkie
4 changes: 4 additions & 0 deletions config/goto.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
'direct_username' => env('GOTO_DIRECT_USERNAME', 'someUsername'),
'direct_password' => env('GOTO_DIRECT_PASSWORD', 'somePassword'),

'legacy' => env('GOTO_LEGACY', false),
'authorization_code' => env('GOTO_AUTHORIZATION_CODE', null),
'redirect_uri' => env('GOTO_REDIRECT_URI', 'someRedirectUri'),

'subject_suffix' => env('GOTO_SUBJECT_SUFFIX', null),
'webinar_link' => env('GOTO_WEBINAR_LINK', 'https://global.gotowebinar.com/manageWebinar.tmpl?webinar=%s'),
];
25 changes: 22 additions & 3 deletions src/Client/Authenticable.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function authenticate()
$this->refreshAccessToken();
} else {
//Perform fresh authentication for bearer and refresh token
$this->authenticateDirect();
$this->authenticateClient();
}
}

Expand All @@ -47,6 +47,13 @@ private function refreshAccessToken()
return $response;
}

private function authenticateClient()
{
return !config('goto.legacy', false) ?
$this->authenticateCode() :
$this->authenticateDirect();
}

private function authenticateDirect()
{
$response = $this->sendAuthenticationRequest([
Expand All @@ -61,12 +68,24 @@ private function authenticateDirect()
return $response;
}

private function authenticateCode()
{
$response = $this->sendAuthenticationRequest([
'grant_type' => 'authorization_code',
'redirect_uri' => config('goto.redirect_uri'),
'code' => config('goto.authorization_code'),
]);

$this->setAccessInformation($response);

return $response;
}

private function sendAuthenticationRequest(array $payload)
{
$this->response = Request::post($this->directAuthenticationUrl)
$this->response = Request::post($this->directAuthenticationUrl, http_build_query($payload), 'form')
->strictSSL($this->strict_ssl)
->addHeaders($this->getAuthenticationHeader())
->body(http_build_query($payload), 'form')
->timeout($this->timeout)
->expectsJson()
->send();
Expand Down
2 changes: 1 addition & 1 deletion src/Client/GotoClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public function get()
$path = $this->buildUrl($this->path, $this->parameters);

Log::info('GotoWebinar:', ['verb' => 'GET', 'path' => $path]);

$response = Request::get($path)
->strictSSL($this->strict_ssl)
->addHeaders($this->getAuthorisationHeader())
Expand Down
45 changes: 45 additions & 0 deletions src/Commands/GoToAccessTokenCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace Slakbal\Gotowebinar\Commands;

use Illuminate\Console\Command;
use Slakbal\Gotowebinar\Facade\Webinars;

class GoToAccessTokenCommand extends Command
{

protected $signature = 'goto:access-token {--flush} {--flush-only}';

protected $description = 'Exchange the authorization code for an access-token.';

public function handle()
{
if(($flush_only = $this->option('flush-only')) || $this->option('flush')) {
$result = Webinars::flushAuthentication()->status();
$this->call('cache:clear', );

if($flush_only) {
$this->showResult($result);
return;
}
}

$result = (array)Webinars::authenticate()->status();

if(!empty($result)) {
if(array_key_exists('access_token', $result)) {
$this->info("Access-Token received:\n");
$this->showResult($result);
}
return;
}

$this->error('Failed to retrieve Access-Token');
}

protected function showResult($result)
{
$this->table(['ready', 'access-token', 'refresh_token', 'organiser_key', 'account_key'], [$result]);
}

}
45 changes: 45 additions & 0 deletions src/Commands/GoToGenerateLinkCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace Slakbal\Gotowebinar\Commands;

use Illuminate\Console\Command;

class GoToGenerateLinkCommand extends Command
{

protected $signature = 'goto:generate-link {--state=}';

protected $description = 'Generate an authorization link to receive an authorization code.
{--state} Pass a state to prevent cross-site request forgery';

protected $scheme = 'https://api.getgo.com/oauth/v2/authorize?client_id={client_id}&response_type=code&redirect_uri={redirect_uri}{state}';

public function handle()
{
$state = $this->option('state', null);

// clear cache first
$this->call('cache:clear');

$link = str_replace([
'{client_id}',
'{redirect_uri}',
'{state}'
], [
config('goto.client_id'),
// @todo: check if null?
config('goto.redirect_uri'),
$state ? "&state={$state}" : ''
], $this->scheme);

$this->info("Click (or copy/paste) the following link to receive an authorization code.\n".
"If your browser is not logged in, you need to login once with your credentials.\n".
"The returned authorization code will be exchanged for an access-token and\n".
"invalidates after the exchange.\n\n".
"Read for more info: https://developer.goto.com/guides/HowTos/03_HOW_accessToken/\n\n");
$this->line('-- Authorization link: ---------------------');
$this->line($link);
$this->line('============================================');
}

}
13 changes: 11 additions & 2 deletions src/GotoWebinarServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

namespace Slakbal\Gotowebinar;

use Illuminate\Support\Facades\App;
use Illuminate\Support\ServiceProvider;
use Slakbal\Gotowebinar\Commands\GoToAccessTokenCommand;
use Slakbal\Gotowebinar\Commands\GoToAuthorizeLinkCommand;
use Slakbal\Gotowebinar\Commands\GoToGenerateLinkCommand;
use Slakbal\Gotowebinar\Resources\Attendee\Attendee;
use Slakbal\Gotowebinar\Resources\Registrant\Registrant;
use Slakbal\Gotowebinar\Resources\Session\Session;
Expand All @@ -17,11 +19,18 @@ class GotoWebinarServiceProvider extends ServiceProvider
*/
public function boot()
{
if (! App::environment('production')) {
if (! $this->app->environment('production')) {
$this->loadRoutesFrom(__DIR__.'/Routes/routes.php');
}

$this->publishes([__DIR__.'/../config/goto.php' => config_path('goto.php')], 'config');

if ($this->app->runningInConsole()) {
$this->commands([
GoToGenerateLinkCommand::class,
GoToAccessTokenCommand::class
]);
}
}

public function register()
Expand Down
4 changes: 3 additions & 1 deletion src/Traits/RequestHelpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Slakbal\Gotowebinar\Traits;

use Illuminate\Support\Arr;

trait RequestHelpers
{
/**
Expand Down Expand Up @@ -37,7 +39,7 @@ public function toArray2()
//list of variables to be filtered out
$ignore = $this->getPayloadExclusions();

return array_where(get_object_vars($this), function ($value, $key) use ($ignore) {
return Arr::where(get_object_vars($this), function ($value, $key) use ($ignore) {
if (! in_array($key, $ignore)) {
return ! empty($value);
}
Expand Down