diff --git a/ProcessMaker/Http/Controllers/Admin/DevLinkController.php b/ProcessMaker/Http/Controllers/Admin/DevLinkController.php index 5c319adcb2..296419925b 100644 --- a/ProcessMaker/Http/Controllers/Admin/DevLinkController.php +++ b/ProcessMaker/Http/Controllers/Admin/DevLinkController.php @@ -36,20 +36,24 @@ public function getOauthClient(Request $request) $devLinkId = $request->input('devlink_id'); $redirectUri = $request->input('redirect_uri'); - $client = Client::where([ + // We can't re-use a client because the secret is hashed. + Client::where([ 'name' => 'devlink', 'redirect' => $redirectUri, - ])->first(); + ]) + ->get() + ->each(function ($c) { + $c->delete(); + }); - if (!$client) { - $clientRepository = app('Laravel\Passport\ClientRepository'); - $client = $clientRepository->createAuthorizationCodeGrantClient('devlink', [$redirectUri]); - } + $clientRepository = app('Laravel\Passport\ClientRepository'); + $client = $clientRepository->createAuthorizationCodeGrantClient('devlink', [$redirectUri]); + $plainSecret = $client->plainSecret; $query = http_build_query([ 'devlink_id' => $devLinkId, 'client_id' => $client->id, - 'client_secret' => $client->secret, + 'client_secret' => $plainSecret, ]); return redirect($redirectUri . '?' . $query); diff --git a/tests/Feature/Admin/DevLinkTest.php b/tests/Feature/Admin/DevLinkTest.php index d1d41b0fbc..f3e6842e7b 100644 --- a/tests/Feature/Admin/DevLinkTest.php +++ b/tests/Feature/Admin/DevLinkTest.php @@ -83,12 +83,16 @@ public function testGetOauthClient() $response = $this->webCall('GET', $url); $response->assertStatus(302); + $locationHeader = $response->headers->get('Location'); + $queryString = parse_url($locationHeader, PHP_URL_QUERY); + parse_str($queryString, $queryParams); + $clientSecretQueryParam = $queryParams['client_secret'] ?? ''; $lastCreatedClient = Client::orderBy('id', 'desc')->first(); $expectedParams = [ 'devlink_id' => $devLink->id, 'client_id' => $lastCreatedClient->id, - 'client_secret' => $lastCreatedClient->secret, + 'client_secret' => $clientSecretQueryParam, ]; $response->assertRedirect(route('devlink.index', $expectedParams)); } diff --git a/upgrades/2026_04_22_200226_encrypt_client_secrets.php b/upgrades/2026_04_22_200226_encrypt_client_secrets.php new file mode 100644 index 0000000000..f960a944b3 --- /dev/null +++ b/upgrades/2026_04_22_200226_encrypt_client_secrets.php @@ -0,0 +1,51 @@ + true]); + $output = Artisan::output(); + if (!str_contains($output, 'All client secrets were successfully hashed')) { + throw new RuntimeException('Failed to hash client secrets. Output: ' . $output); + } + } + + /** + * Reverse the upgrade migration. + * + * @return void + */ + public function down() + { + // + } +}