Skip to content

Commit 4fc7618

Browse files
authored
Merge pull request #207 from pdsinterop/fix/refreshToken
handle token request cases for authorization_code and refresh_token differently
2 parents 603316c + 4c64f33 commit 4fc7618

File tree

8 files changed

+122
-24
lines changed

8 files changed

+122
-24
lines changed

.config/phpcs.xml.dist

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@
2020
<rule ref="PHPCompatibility"/>
2121
<config name="testVersion" value="8.0-"/>
2222

23+
<!-- Set indent for `break` to 0 so it aligns with `case` and `default` -->
24+
<rule ref="PSR2">
25+
<exclude name="PSR2.ControlStructures.SwitchDeclaration"/>
26+
<exclude name="PSR2.ControlStructures.ElseIfDeclaration.NotAllowed"/>
27+
<exclude name="PSR2.ControlStructures.ControlStructureSpacing.SpacingAfterOpenBrace"/>
28+
</rule>
29+
2330
<!-- Include the whole PSR-12 standard -->
2431
<rule ref="PSR12">
2532
<!-- Until things have been cleaned up a bit, these violations are allowed -->

init.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ php console.php config:system:set trusted_domains 3 --value=thirdparty
99
# set 'tester' and 'https://tester' as allowed clients for the test suite to run
1010
php console.php user:setting alice solid allowedClients '["f5d1278e8109edd94e1e4197e04873b9", "2e5cddcf0f663544e98982931e6cc5a6"]'
1111
echo configured
12+
mkdir -p /var/www/html/data/files_trashbin/versions

solid/appinfo/info.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ When you do this, the Solid App can store data in your Nextcloud account through
1616
<author mail="[email protected]" >Auke van Slooten</author>
1717
<namespace>Solid</namespace>
1818
<category>integration</category>
19+
<types>
20+
<authentication/>
21+
</types>
1922
<bugs>https://github.com/pdsinterop/solid-nextcloud/issues</bugs>
2023
<dependencies>
2124
<nextcloud min-version="28" max-version="30"/>

solid/lib/AppInfo/Application.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,31 @@
1010
use OCA\Solid\Service\SolidWebhookService;
1111
use OCA\Solid\Db\SolidWebhookMapper;
1212
use OCA\Solid\Middleware\SolidCorsMiddleware;
13+
use OCA\Solid\ClientAuth;
1314

1415
use OCP\AppFramework\App;
1516
use OCP\AppFramework\Bootstrap\IBootContext;
1617
use OCP\AppFramework\Bootstrap\IBootstrap;
1718
use OCP\AppFramework\Bootstrap\IRegistrationContext;
1819
use OCP\IDBConnection;
20+
use OCP\IRequest;
21+
use OCP\Server;
1922

2023
class Application extends App implements IBootstrap {
2124
public const APP_ID = 'solid';
22-
public static $userSubDomainsEnabled;
25+
public static $userSubDomainsEnabled;
2326

2427
/**
2528
* @param array $urlParams
2629
*/
2730
public function __construct(array $urlParams = []) {
31+
$request = \OCP\Server::get(\OCP\IRequest::class);
32+
$rawPathInfo = $request->getRawPathInfo();
33+
34+
if ($rawPathInfo == '/apps/solid/token') {
35+
$backend = new \OCA\Solid\ClientAuth();
36+
\OC::$server->getUserManager()->registerBackend($backend);
37+
}
2838
parent::__construct(self::APP_ID, $urlParams);
2939
}
3040

solid/lib/ClientAuth.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
/*
3+
IMPORTANT WARNING!
4+
5+
This class is a user backend that accepts 'all'.
6+
Any user, and password is currently accepted as true.
7+
8+
The reason this is here is that Solid clients will use basic
9+
authentication to do a POST request to the token endpoint,
10+
where the actual authorization happens.
11+
12+
The security for this user backend lies in the fact that it
13+
is only activated for the token endpoint in the Solid app.
14+
15+
In /lib/AppInfo/Application.php there is a check for the
16+
token endpoint before this thing activates.
17+
18+
It is completely unsuitable as an actual user backend in the
19+
normal sense of the word.
20+
21+
It is here to allow the token requests with basic
22+
authentication requests to pass to us.
23+
*/
24+
25+
namespace OCA\Solid;
26+
27+
use OCP\User\Backend\ABackend;
28+
use OCP\User\Backend\ICheckPasswordBackend;
29+
30+
/**
31+
* @package OCA\Solid
32+
*/
33+
class ClientAuth extends ABackend implements ICheckPasswordBackend {
34+
public function __construct() {
35+
}
36+
37+
public function checkPassword(string $username, string $password) {
38+
return true;
39+
}
40+
41+
public function getBackendName() {
42+
return "Solid";
43+
}
44+
public function deleteUser($uid) {
45+
return false;
46+
}
47+
public function getUsers($search = "", $limit = null, $offset = null, $callback = null) {
48+
return [];
49+
}
50+
public function userExists($uid) {
51+
return true;
52+
}
53+
public function getDisplayName($uid) {
54+
return "Solid client";
55+
}
56+
public function getDisplayNames($search = "", $limit = null, $offset = null) {
57+
return [];
58+
}
59+
public function hasUserListings() {
60+
return false;
61+
}
62+
}

solid/lib/Controller/ServerController.php

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,14 @@ private function getKeys() {
105105
}
106106

107107
private function createAuthServerConfig() {
108-
$clientId = isset($_GET['client_id']) ? $_GET['client_id'] : null;
108+
$clientId = null;
109+
if (isset($_GET['client_id'])) {
110+
$clientId = $_GET['client_id'];
111+
} else if (isset($_POST['client_id'])) {
112+
if (isset($_POST['refresh_token'])) { // FIXME: Why does the test suite break without this?
113+
$clientId = $_POST['client_id'];
114+
}
115+
}
109116
$client = $this->getClient($clientId);
110117
$keys = $this->getKeys();
111118
try {
@@ -316,7 +323,25 @@ public function session() {
316323
*/
317324
public function token() {
318325
$request = \Laminas\Diactoros\ServerRequestFactory::fromGlobals($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES);
319-
$code = $request->getParsedBody()['code'];
326+
$grantType = $request->getParsedBody()['grant_type'];
327+
switch ($grantType) {
328+
case "authorization_code":
329+
$code = $request->getParsedBody()['code'];
330+
// FIXME: not sure if decoding this here is the way to go.
331+
// FIXME: because this is a public page, the nonce from the session is not available here.
332+
$codeInfo = $this->tokenGenerator->getCodeInfo($code);
333+
$userId = $codeInfo['user_id'];
334+
break;
335+
case "refresh_token":
336+
$refreshToken = $request->getParsedBody()['refresh_token'];
337+
$tokenInfo = $this->tokenGenerator->getCodeInfo($refreshToken); // FIXME: getCodeInfo should be named 'decrypt' or 'getInfo'?
338+
$userId = $tokenInfo['user_id'];
339+
break;
340+
default:
341+
$userId = false;
342+
break;
343+
}
344+
320345
$clientId = $request->getParsedBody()['client_id'];
321346

322347
$httpDpop = $request->getServerParams()['HTTP_DPOP'];
@@ -325,17 +350,16 @@ public function token() {
325350
$server = new \Pdsinterop\Solid\Auth\Server($this->authServerFactory, $this->authServerConfig, $response);
326351
$response = $server->respondToAccessTokenRequest($request);
327352

328-
// FIXME: not sure if decoding this here is the way to go.
329-
// FIXME: because this is a public page, the nonce from the session is not available here.
330-
$codeInfo = $this->tokenGenerator->getCodeInfo($code);
331-
$response = $this->tokenGenerator->addIdTokenToResponse(
332-
$response,
333-
$clientId,
334-
$codeInfo['user_id'],
335-
($_SESSION['nonce'] ?? ''),
336-
$this->config->getPrivateKey(),
337-
$httpDpop
338-
);
353+
if ($userId) {
354+
$response = $this->tokenGenerator->addIdTokenToResponse(
355+
$response,
356+
$clientId,
357+
$userId,
358+
($_SESSION['nonce'] ?? ''),
359+
$this->config->getPrivateKey(),
360+
$httpDpop
361+
);
362+
}
339363

340364
return $this->respond($response); // ->addHeader('Access-Control-Allow-Origin', '*');
341365
}
@@ -380,14 +404,7 @@ public function register() {
380404
$clientData = $this->config->saveClientRegistration($origin, $clientData);
381405
$registration = array(
382406
'client_id' => $clientData['client_id'],
383-
/*
384-
FIXME: returning client_secret will trigger calls with basic auth to us. To get this to work, we need this patch:
385-
// File /var/www/vhosts/solid-nextcloud/site/www/lib/base.php not changed so no update needed
386-
// ($request->getRawPathInfo() !== '/apps/oauth2/api/v1/token') &&
387-
// ($request->getRawPathInfo() !== '/apps/solid/token')
388-
*/
389-
// 'client_secret' => $clientData['client_secret'], // FIXME: Returning this means we need to patch Nextcloud to accept tokens on calls to
390-
407+
'client_secret' => $clientData['client_secret'],
391408
'registration_client_uri' => $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("solid.server.registeredClient", array("clientId" => $clientData['client_id']))),
392409
'client_id_issued_at' => $clientData['client_id_issued_at'],
393410
'redirect_uris' => $clientData['redirect_uris'],

solid/tests/Integration/AppTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use OCP\AppFramework\App;
66
use Test\TestCase;
77

8-
98
/**
109
* This test shows how to make a small Integration Test. Query your class
1110
* directly from the container, only pass in mocks if needed and run your tests

solid/tests/Unit/Controller/ServerControllerTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use PHPUnit\Framework\MockObject\MockObject;
1717
use PHPUnit\Framework\TestCase;
1818

19-
2019
function file_get_contents($filename)
2120
{
2221
if ($filename === 'php://input') {

0 commit comments

Comments
 (0)