Skip to content

Commit 486b4ec

Browse files
authored
Merge pull request #740 from nextcloud/enh/make-pkce-optional
Make pkce optional
2 parents 9a5e996 + fe0de68 commit 486b4ec

2 files changed

Lines changed: 35 additions & 15 deletions

File tree

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,17 @@ parameter to the login URL.
6464
sudo -u www-data php var/www/nextcloud/occ config:app:set --value=0 user_oidc allow_multiple_user_backends
6565
```
6666

67+
### PKCE
68+
69+
This app supports PKCE (Proof Key for Code Exchange).
70+
https://datatracker.ietf.org/doc/html/rfc7636
71+
It is disabled by default and can be enabled in `config.php`:
72+
``` php
73+
'user_oidc' => [
74+
'use_pkce' => true,
75+
],
76+
```
77+
6778
### Single logout
6879

6980
Single logout is enabled by default. When logging out of Nextcloud,

lib/Controller/LoginController.php

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -230,9 +230,13 @@ public function login(int $providerId, string $redirectUrl = null) {
230230
$nonce = $this->random->generate(32, ISecureRandom::CHAR_DIGITS . ISecureRandom::CHAR_UPPER);
231231
$this->session->set(self::NONCE, $nonce);
232232

233-
// PKCE code_challenge see https://datatracker.ietf.org/doc/html/rfc7636
234-
$code_verifier = $this->random->generate(128, ISecureRandom::CHAR_DIGITS . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER);
235-
$this->session->set(self::CODE_VERIFIER, $code_verifier);
233+
$oidcSystemConfig = $this->config->getSystemValue('user_oidc', []);
234+
$isPkceEnabled = isset($oidcSystemConfig['use_pkce']) && $oidcSystemConfig['use_pkce'];
235+
if ($isPkceEnabled) {
236+
// PKCE code_challenge see https://datatracker.ietf.org/doc/html/rfc7636
237+
$code_verifier = $this->random->generate(128, ISecureRandom::CHAR_DIGITS . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER);
238+
$this->session->set(self::CODE_VERIFIER, $code_verifier);
239+
}
236240

237241
$this->session->set(self::PROVIDERID, $providerId);
238242
$this->session->close();
@@ -285,9 +289,11 @@ public function login(int $providerId, string $redirectUrl = null) {
285289
'claims' => json_encode($claims),
286290
'state' => $state,
287291
'nonce' => $nonce,
288-
'code_challenge' => $this->toCodeChallenge($code_verifier),
289-
'code_challenge_method' => 'S256',
290292
];
293+
if ($isPkceEnabled) {
294+
$data['code_challenge'] = $this->toCodeChallenge($code_verifier);
295+
$data['code_challenge_method'] = 'S256';
296+
}
291297
// pass discovery query parameters also on to the authentication
292298
$discoveryUrl = parse_url($provider->getDiscoveryEndpoint());
293299
if (isset($discoveryUrl['query'])) {
@@ -375,25 +381,29 @@ public function code(string $state = '', string $code = '', string $scope = '',
375381
return $this->buildErrorTemplateResponse($message, Http::STATUS_BAD_REQUEST, [], false);
376382
}
377383

378-
$code_verifier = $this->session->get(self::CODE_VERIFIER);
384+
$oidcSystemConfig = $this->config->getSystemValue('user_oidc', []);
385+
$isPkceEnabled = isset($oidcSystemConfig['use_pkce']) && $oidcSystemConfig['use_pkce'];
379386

380387
$discovery = $this->discoveryService->obtainDiscovery($provider);
381388

382389
$this->logger->debug('Obtainting data from: ' . $discovery['token_endpoint']);
383390

384391
$client = $this->clientService->newClient();
385392
try {
393+
$requestBody = [
394+
'code' => $code,
395+
'client_id' => $provider->getClientId(),
396+
'client_secret' => $providerClientSecret,
397+
'redirect_uri' => $this->urlGenerator->linkToRouteAbsolute(Application::APP_ID . '.login.code'),
398+
'grant_type' => 'authorization_code',
399+
];
400+
if ($isPkceEnabled) {
401+
$requestBody['code_verifier'] = $this->session->get(self::CODE_VERIFIER); // Set for the PKCE flow
402+
}
386403
$result = $client->post(
387404
$discovery['token_endpoint'],
388405
[
389-
'body' => [
390-
'code' => $code,
391-
'client_id' => $provider->getClientId(),
392-
'client_secret' => $providerClientSecret,
393-
'redirect_uri' => $this->urlGenerator->linkToRouteAbsolute(Application::APP_ID . '.login.code'),
394-
'grant_type' => 'authorization_code',
395-
'code_verifier' => $code_verifier, // Set for the PKCE flow
396-
],
406+
'body' => $requestBody,
397407
]
398408
);
399409
} catch (ClientException | ServerException $e) {
@@ -480,7 +490,6 @@ public function code(string $state = '', string $code = '', string $scope = '',
480490
return $this->build403TemplateResponse($message, Http::STATUS_BAD_REQUEST, ['reason' => 'failed to provision user']);
481491
}
482492

483-
$oidcSystemConfig = $this->config->getSystemValue('user_oidc', []);
484493
$autoProvisionAllowed = (!isset($oidcSystemConfig['auto_provision']) || $oidcSystemConfig['auto_provision']);
485494

486495
// Provisioning

0 commit comments

Comments
 (0)