Refactor slightly the indieauth code

This commit is contained in:
Jonny Barnes 2017-03-01 20:59:09 +00:00
parent 4f57905bcc
commit fe8f871e3a
5 changed files with 70 additions and 59 deletions

View file

@ -2,7 +2,6 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use IndieAuth\Client;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Services\TokenService; use App\Services\TokenService;
use App\Services\IndieAuthService; use App\Services\IndieAuthService;
@ -14,11 +13,6 @@ class IndieAuthController extends Controller
*/ */
protected $indieAuthService; protected $indieAuthService;
/**
* The IndieAuth Client implementation.
*/
protected $client;
/** /**
* The Token handling service. * The Token handling service.
*/ */
@ -28,16 +22,14 @@ class IndieAuthController extends Controller
* Inject the dependencies. * Inject the dependencies.
* *
* @param \App\Services\IndieAuthService $indieAuthService * @param \App\Services\IndieAuthService $indieAuthService
* @param \IndieAuth\Client $client * @param \App\Services\TokenService $tokenService
* @return void * @return void
*/ */
public function __construct( public function __construct(
IndieAuthService $indieAuthService = null, IndieAuthService $indieAuthService = null,
Client $client = null,
TokenService $tokenService = null TokenService $tokenService = null
) { ) {
$this->indieAuthService = $indieAuthService ?? new IndieAuthService(); $this->indieAuthService = $indieAuthService ?? new IndieAuthService();
$this->client = $client ?? new Client();
$this->tokenService = $tokenService ?? new TokenService(); $this->tokenService = $tokenService ?? new TokenService();
} }
@ -53,21 +45,19 @@ class IndieAuthController extends Controller
public function start(Request $request) public function start(Request $request)
{ {
$authorizationEndpoint = $this->indieAuthService->getAuthorizationEndpoint( $authorizationEndpoint = $this->indieAuthService->getAuthorizationEndpoint(
$request->input('me'), $request->input('me')
$this->client
); );
if ($authorizationEndpoint) { if ($authorizationEndpoint !== null) {
$authorizationURL = $this->indieAuthService->buildAuthorizationURL( $authorizationURL = $this->indieAuthService->buildAuthorizationURL(
$authorizationEndpoint, $authorizationEndpoint,
$request->input('me'), $request->input('me')
$this->client
); );
if ($authorizationURL) { if ($authorizationURL) {
return redirect($authorizationURL); return redirect($authorizationURL);
} }
} }
return redirect(route('micropub-client'))->withErrors('Unable to determine authorisation endpoint', 'indieauth'); return redirect(route('micropub-client'))->with('error', 'Unable to determine authorisation endpoint');
} }
/** /**
@ -80,14 +70,17 @@ class IndieAuthController extends Controller
public function callback(Request $request) public function callback(Request $request)
{ {
if ($request->session()->get('state') != $request->input('state')) { if ($request->session()->get('state') != $request->input('state')) {
return redirect(route('micropub-client'))->withErrors( return redirect(route('micropub-client'))->with(
'Invalid <code>state</code> value returned from indieauth server', 'error',
'indieauth' 'Invalid <code>state</code> value returned from indieauth server'
); );
} }
$tokenEndpoint = $this->indieAuthService->getTokenEndpoint($request->input('me'), $this->client); $tokenEndpoint = $this->indieAuthService->getTokenEndpoint($request->input('me'));
if ($tokenEndpoint === false) { if ($tokenEndpoint === false) {
return redirect(route('micropub-client'))->withErrors('Unable to determine token endpoint', 'indieauth'); return redirect(route('micropub-client'))->with(
'error',
'Unable to determine token endpoint'
);
} }
$data = [ $data = [
'endpoint' => $tokenEndpoint, 'endpoint' => $tokenEndpoint,
@ -97,7 +90,7 @@ class IndieAuthController extends Controller
'client_id' => route('micropub-client'), 'client_id' => route('micropub-client'),
'state' => $request->input('state'), 'state' => $request->input('state'),
]; ];
$token = $this->indieAuthService->getAccessToken($data, $this->client); $token = $this->indieAuthService->getAccessToken($data);
if (array_key_exists('access_token', $token)) { if (array_key_exists('access_token', $token)) {
$request->session()->put('me', $token['me']); $request->session()->put('me', $token['me']);
@ -106,7 +99,10 @@ class IndieAuthController extends Controller
return redirect(route('micropub-client')); return redirect(route('micropub-client'));
} }
return redirect(route('micropub-client'))->withErrors('Unable to get a token from the endpoint', 'indieauth'); return redirect(route('micropub-client'))->with(
'error',
'Unable to get a token from the endpoint'
);
} }
/** /**
@ -136,7 +132,7 @@ class IndieAuthController extends Controller
'client_id' => $request->input('client_id'), 'client_id' => $request->input('client_id'),
'state' => $request->input('state'), 'state' => $request->input('state'),
]; ];
$auth = $this->indieAuthService->verifyIndieAuthCode($authData, $this->client); $auth = $this->indieAuthService->verifyIndieAuthCode($authData);
if (array_key_exists('me', $auth)) { if (array_key_exists('me', $auth)) {
$scope = $auth['scope'] ?? ''; $scope = $auth['scope'] ?? '';
$tokenData = [ $tokenData = [

View file

@ -1,20 +1,34 @@
<?php <?php
declare(strict_types=1);
namespace App\Services; namespace App\Services;
use IndieAuth\Client;
class IndieAuthService class IndieAuthService
{ {
protected $client;
public function __construct()
{
$this->client = new Client();
}
/** /**
* Given a domain, determing the assocaited authorization endpoint, * Given a domain, determing the assocaited authorization endpoint,
* if one exists. * if one exists.
* *
* @param string The domain * @param string The domain
* @param \IndieAuth\Client $client
* @return string|null * @return string|null
*/ */
public function getAuthorizationEndpoint($domain, $client) public function getAuthorizationEndpoint(string $domain): ?string
{ {
return $client->discoverAuthorizationEndpoint($client->normalizeMeURL($domain)); $endpoint = $this->client->discoverAuthorizationEndpoint($this->client->normalizeMeURL($domain));
if ($endpoint === false) {
return null;
}
return $endpoint;
} }
/** /**
@ -22,20 +36,18 @@ class IndieAuthService
* *
* @param string $authEndpoint * @param string $authEndpoint
* @param string $domain * @param string $domain
* @param \IndieAuth\Client $client
* @return string * @return string
*/ */
public function buildAuthorizationURL($authEndpoint, $domain, $client) public function buildAuthorizationURL(string $authEndpoint, string $domain): string
{ {
$domain = $client->normalizeMeURL($domain);
$state = bin2hex(openssl_random_pseudo_bytes(16)); $state = bin2hex(openssl_random_pseudo_bytes(16));
session(['state' => $state]); session(['state' => $state]);
$redirectURL = route('indieauth-callback'); $redirectURL = route('indieauth-callback');
$clientId = route('micropub-client'); $clientId = route('micropub-client');
$scope = 'post'; $scope = 'post';
$authorizationURL = $client->buildAuthorizationURL( $authorizationURL = $this->client->buildAuthorizationURL(
$authEndpoint, $authEndpoint,
$domain, $this->client->normalizeMeURL($domain),
$redirectURL, $redirectURL,
$clientId, $clientId,
$state, $state,
@ -49,24 +61,22 @@ class IndieAuthService
* Discover the token endpoint for a given domain. * Discover the token endpoint for a given domain.
* *
* @param string The domain * @param string The domain
* @param \IndieAuth\Client $client
* @return string|null * @return string|null
*/ */
public function getTokenEndpoint($domain, $client) public function getTokenEndpoint(string $domain): ?string
{ {
return $client->discoverTokenEndpoint($domain); return $this->client->discoverTokenEndpoint($this->client->normalizeMeURL($domain));
} }
/** /**
* Retrieve a token from the token endpoint. * Retrieve a token from the token endpoint.
* *
* @param array The relavent data * @param array The relavent data
* @param \IndieAuth\Client $client
* @return array * @return array
*/ */
public function getAccessToken(array $data, $client) public function getAccessToken(array $data): array
{ {
return $client->getAccessToken( return $this->client->getAccessToken(
$data['endpoint'], $data['endpoint'],
$data['code'], $data['code'],
$data['me'], $data['me'],
@ -81,14 +91,13 @@ class IndieAuthService
* valid. * valid.
* *
* @param array The data. * @param array The data.
* @param \IndieAuth\Client $client
* @return array|null * @return array|null
*/ */
public function verifyIndieAuthCode(array $data, $client) public function verifyIndieAuthCode(array $data): ?array
{ {
$authEndpoint = $client->discoverAuthorizationEndpoint($data['me']); $authEndpoint = $this->client->discoverAuthorizationEndpoint($data['me']);
if ($authEndpoint) { if ($authEndpoint) {
return $client->verifyIndieAuthCode( return $this->client->verifyIndieAuthCode(
$authEndpoint, $authEndpoint,
$data['code'], $data['code'],
$data['me'], $data['me'],
@ -103,11 +112,10 @@ class IndieAuthService
* Determine the micropub endpoint. * Determine the micropub endpoint.
* *
* @param string $domain * @param string $domain
* @param \IndieAuth\Client $client * @return string|null The endpoint
* @return string The endpoint
*/ */
public function discoverMicropubEndpoint($domain, $client) public function discoverMicropubEndpoint(string $domain): ?string
{ {
return $client->discoverMicropubEndpoint($client->normalizeMeURL($domain)); return $this->client->discoverMicropubEndpoint($this->client->normalizeMeURL($domain));
} }
} }

View file

@ -109,7 +109,7 @@ Route::group(['domain' => config('url.longurl')], function () {
Route::get('notes/tagged/{tag}', 'NotesController@tagged'); Route::get('notes/tagged/{tag}', 'NotesController@tagged');
//indieauth //indieauth
Route::any('indieauth/start', 'IndieAuthController@start')->name('indieauth-start'); Route::post('indieauth/start', 'IndieAuthController@start')->name('indieauth-start');
Route::get('indieauth/callback', 'IndieAuthController@callback')->name('indieauth-callback'); Route::get('indieauth/callback', 'IndieAuthController@callback')->name('indieauth-callback');
Route::get('logout', 'IndieAuthController@logout')->name('indieauth-logout'); Route::get('logout', 'IndieAuthController@logout')->name('indieauth-logout');
Route::post('api/token', 'IndieAuthController@tokenEndpoint'); //hmmm? Route::post('api/token', 'IndieAuthController@tokenEndpoint'); //hmmm?

View file

@ -15,8 +15,8 @@ class IndieAuthControllerTest extends TestCase
*/ */
public function test_indieauthcontroller_begin_auth_flow_redirects_back_to_client_on_error() public function test_indieauthcontroller_begin_auth_flow_redirects_back_to_client_on_error()
{ {
$response = $this->call('GET', '/indieauth/start', ['me' => 'http://example.org']); $response = $this->call('POST', '/indieauth/start', ['me' => 'http://example.org']);
$this->assertSame(config('app.url') . '/micropub/create', $response->headers->get('Location')); $this->assertSame(route('micropub-client'), $response->headers->get('Location'));
} }
/** /**
@ -26,7 +26,7 @@ class IndieAuthControllerTest extends TestCase
*/ */
public function test_indieauthcontroller_begin_auth_redirects_to_endpoint() public function test_indieauthcontroller_begin_auth_redirects_to_endpoint()
{ {
$response = $this->call('GET', '/indieauth/start', ['me' => config('app.url')]); $response = $this->call('POST', '/indieauth/start', ['me' => config('app.url')]);
$this->assertSame( $this->assertSame(
'https://indieauth.com/auth?me=', 'https://indieauth.com/auth?me=',
substr($response->headers->get('Location'), 0, 30) substr($response->headers->get('Location'), 0, 30)
@ -46,6 +46,6 @@ class IndieAuthControllerTest extends TestCase
'indieauth/callback', 'indieauth/callback',
['me', config('app.url'), 'state' => 'request-session'] ['me', config('app.url'), 'state' => 'request-session']
); );
$response->assertSessionHasErrors(); $response->assertSessionHas(['error' => 'Invalid <code>state</code> value returned from indieauth server']);
} }
} }

View file

@ -16,11 +16,22 @@ class IndieAuthServiceTest extends TestCase
public function test_indieauthservice_getauthorizationendpoint_method() public function test_indieauthservice_getauthorizationendpoint_method()
{ {
$service = new \App\Services\IndieAuthService(); $service = new \App\Services\IndieAuthService();
$client = new \IndieAuth\Client(); $result = $service->getAuthorizationEndpoint(config('app.url'));
$result = $service->getAuthorizationEndpoint(config('app.url'), $client);
$this->assertEquals('https://indieauth.com/auth', $result); $this->assertEquals('https://indieauth.com/auth', $result);
} }
/**
* Test the getAuthorizationEndpoint method returns null on failure.
*
* @return void
*/
public function test_indieauthservice_getauthorizationendpoint_method_returns_null_on_failure()
{
$service = new \App\Services\IndieAuthService();
$result = $service->getAuthorizationEndpoint('http://example.org');
$this->assertEquals(null, $result);
}
/** /**
* Test that the Service build the correct redirect URL. * Test that the Service build the correct redirect URL.
* *
@ -29,11 +40,9 @@ class IndieAuthServiceTest extends TestCase
public function test_indieauthservice_builds_correct_redirect_url() public function test_indieauthservice_builds_correct_redirect_url()
{ {
$service = new \App\Services\IndieAuthService(); $service = new \App\Services\IndieAuthService();
$client = new \IndieAuth\Client();
$result = $service->buildAuthorizationURL( $result = $service->buildAuthorizationURL(
'https://indieauth.com/auth', 'https://indieauth.com/auth',
config('app.url'), config('app.url')
$client
); );
$this->assertEquals( $this->assertEquals(
'https://indieauth.com/auth?me=', 'https://indieauth.com/auth?me=',
@ -49,8 +58,7 @@ class IndieAuthServiceTest extends TestCase
public function test_indieauthservice_gettokenendpoint_method() public function test_indieauthservice_gettokenendpoint_method()
{ {
$service = new \App\Services\IndieAuthService(); $service = new \App\Services\IndieAuthService();
$client = new \IndieAuth\Client(); $result = $service->getTokenEndpoint(config('app.url'));
$result = $service->getTokenEndpoint(config('app.url'), $client);
$this->assertEquals(config('app.url') . '/api/token', $result); $this->assertEquals(config('app.url') . '/api/token', $result);
} }
@ -62,8 +70,7 @@ class IndieAuthServiceTest extends TestCase
public function test_indieauthservice_discovermicropubendpoint_method() public function test_indieauthservice_discovermicropubendpoint_method()
{ {
$service = new \App\Services\IndieAuthService(); $service = new \App\Services\IndieAuthService();
$client = new \IndieAuth\Client(); $result = $service->discoverMicropubEndpoint(config('app.url'));
$result = $service->discoverMicropubEndpoint(config('app.url'), $client);
$this->assertEquals(config('app.url') . '/api/post', $result); $this->assertEquals(config('app.url') . '/api/post', $result);
} }
} }