Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.
   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  namespace core\oauth2\discovery;
  18  
  19  use core\http_client;
  20  use GuzzleHttp\Exception\ClientException;
  21  use GuzzleHttp\Handler\MockHandler;
  22  use GuzzleHttp\HandlerStack;
  23  use GuzzleHttp\Middleware;
  24  use GuzzleHttp\Psr7\Response;
  25  use Psr\Http\Message\ResponseInterface;
  26  
  27  /**
  28   * Unit tests for {@see auth_server_config_reader}.
  29   *
  30   * @coversDefaultClass \core\oauth2\discovery\auth_server_config_reader
  31   * @package core
  32   * @copyright 2023 Jake Dallimore <jrhdallimore@gmail.com>
  33   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  34   */
  35  class auth_server_config_reader_test extends \advanced_testcase {
  36  
  37      /**
  38       * Test reading the config for an auth server.
  39       *
  40       * @covers ::read_configuration
  41       * @dataProvider config_provider
  42       * @param string $issuerurl the auth server issuer URL.
  43       * @param ResponseInterface $httpresponse a stub HTTP response.
  44       * @param null|string $altwellknownsuffix an alternate value for the well known suffix to use in the reader.
  45       * @param array $expected test expectations.
  46       * @return void
  47       */
  48      public function test_read_configuration(string $issuerurl, ResponseInterface $httpresponse, ?string $altwellknownsuffix = null,
  49              array $expected = []) {
  50  
  51          $mock = new MockHandler([$httpresponse]);
  52          $handlerstack = HandlerStack::create($mock);
  53          if (!empty($expected['request'])) {
  54              // Request history tracking to allow asserting that request was sent as expected below (to the stub client).
  55              $container = [];
  56              $history = Middleware::history($container);
  57              $handlerstack->push($history);
  58          }
  59  
  60          $args = [
  61              new http_client(['handler' => $handlerstack]),
  62          ];
  63          if (!is_null($altwellknownsuffix)) {
  64              $args[] = $altwellknownsuffix;
  65          }
  66  
  67          if (!empty($expected['exception'])) {
  68              $this->expectException($expected['exception']);
  69          }
  70          $configreader = new auth_server_config_reader(...$args);
  71          $config = $configreader->read_configuration(new \moodle_url($issuerurl));
  72  
  73          if (!empty($expected['request'])) {
  74              // Verify the request goes to the correct URL (i.e. the well known suffix is correctly positioned).
  75              $this->assertEquals($expected['request']['url'], $container[0]['request']->getUri());
  76          }
  77  
  78          $this->assertEquals($expected['metadata'], (array) $config);
  79      }
  80  
  81      /**
  82       * Provider for testing read_configuration().
  83       *
  84       * @return array test data.
  85       */
  86      public function config_provider(): array {
  87          return [
  88              'Valid, good issuer URL, good config' => [
  89                  'issuer_url' => 'https://app.example.com',
  90                  'http_response' => new Response(
  91                      200,
  92                      ['Content-Type' => 'application/json'],
  93                      json_encode([
  94                          "issuer" => "https://app.example.com",
  95                          "authorization_endpoint" => "https://app.example.com/authorize",
  96                          "token_endpoint" => "https://app.example.com/token",
  97                          "token_endpoint_auth_methods_supported" => [
  98                              "client_secret_basic",
  99                              "private_key_jwt"
 100                          ],
 101                          "token_endpoint_auth_signing_alg_values_supported" => [
 102                              "RS256",
 103                              "ES256"
 104                          ],
 105                          "userinfo_endpoint" => "https://app.example.com/userinfo",
 106                          "jwks_uri" => "https://app.example.com/jwks.json",
 107                          "registration_endpoint" => "https://app.example.com/register",
 108                          "scopes_supported" => [
 109                              "openid",
 110                              "profile",
 111                              "email",
 112                          ],
 113                          "response_types_supported" => [
 114                              "code",
 115                              "code token"
 116                          ],
 117                          "service_documentation" => "http://app.example.com/service_documentation.html",
 118                          "ui_locales_supported" => [
 119                              "en-US",
 120                              "en-GB",
 121                              "fr-FR",
 122                          ]
 123                      ])
 124                  ),
 125                  'well_known_suffix' => null,
 126                  'expected' => [
 127                      'request' => [
 128                          'url' => 'https://app.example.com/.well-known/oauth-authorization-server'
 129                      ],
 130                      'metadata' => [
 131                          "issuer" => "https://app.example.com",
 132                          "authorization_endpoint" => "https://app.example.com/authorize",
 133                          "token_endpoint" => "https://app.example.com/token",
 134                          "token_endpoint_auth_methods_supported" => [
 135                              "client_secret_basic",
 136                              "private_key_jwt"
 137                          ],
 138                          "token_endpoint_auth_signing_alg_values_supported" => [
 139                              "RS256",
 140                              "ES256"
 141                          ],
 142                          "userinfo_endpoint" => "https://app.example.com/userinfo",
 143                          "jwks_uri" => "https://app.example.com/jwks.json",
 144                          "registration_endpoint" => "https://app.example.com/register",
 145                          "scopes_supported" => [
 146                              "openid",
 147                              "profile",
 148                              "email",
 149                          ],
 150                          "response_types_supported" => [
 151                              "code",
 152                              "code token"
 153                          ],
 154                          "service_documentation" => "http://app.example.com/service_documentation.html",
 155                          "ui_locales_supported" => [
 156                              "en-US",
 157                              "en-GB",
 158                              "fr-FR",
 159                          ]
 160                      ]
 161                  ]
 162              ],
 163              'Valid, issuer URL with path component confirming well known suffix placement' => [
 164                  'issuer_url' => 'https://app.example.com/some/path',
 165                  'http_response' => new Response(
 166                      200,
 167                      ['Content-Type' => 'application/json'],
 168                      json_encode([
 169                          "issuer" => "https://app.example.com",
 170                          "authorization_endpoint" => "https://app.example.com/authorize",
 171                          "token_endpoint" => "https://app.example.com/token",
 172                          "token_endpoint_auth_methods_supported" => [
 173                              "client_secret_basic",
 174                              "private_key_jwt"
 175                          ],
 176                          "token_endpoint_auth_signing_alg_values_supported" => [
 177                              "RS256",
 178                              "ES256"
 179                          ],
 180                          "userinfo_endpoint" => "https://app.example.com/userinfo",
 181                          "jwks_uri" => "https://app.example.com/jwks.json",
 182                          "registration_endpoint" => "https://app.example.com/register",
 183                          "scopes_supported" => [
 184                              "openid",
 185                              "profile",
 186                              "email",
 187                          ],
 188                          "response_types_supported" => [
 189                              "code",
 190                              "code token"
 191                          ],
 192                          "service_documentation" => "http://app.example.com/service_documentation.html",
 193                          "ui_locales_supported" => [
 194                              "en-US",
 195                              "en-GB",
 196                              "fr-FR",
 197                          ]
 198                      ])
 199                  ),
 200                  'well_known_suffix' => null,
 201                  'expected' => [
 202                      'request' => [
 203                          'url' => 'https://app.example.com/.well-known/oauth-authorization-server/some/path'
 204                      ],
 205                      'metadata' => [
 206                          "issuer" => "https://app.example.com",
 207                          "authorization_endpoint" => "https://app.example.com/authorize",
 208                          "token_endpoint" => "https://app.example.com/token",
 209                          "token_endpoint_auth_methods_supported" => [
 210                              "client_secret_basic",
 211                              "private_key_jwt"
 212                          ],
 213                          "token_endpoint_auth_signing_alg_values_supported" => [
 214                              "RS256",
 215                              "ES256"
 216                          ],
 217                          "userinfo_endpoint" => "https://app.example.com/userinfo",
 218                          "jwks_uri" => "https://app.example.com/jwks.json",
 219                          "registration_endpoint" => "https://app.example.com/register",
 220                          "scopes_supported" => [
 221                              "openid",
 222                              "profile",
 223                              "email",
 224                          ],
 225                          "response_types_supported" => [
 226                              "code",
 227                              "code token"
 228                          ],
 229                          "service_documentation" => "http://app.example.com/service_documentation.html",
 230                          "ui_locales_supported" => [
 231                              "en-US",
 232                              "en-GB",
 233                              "fr-FR",
 234                          ]
 235                      ]
 236                  ]
 237              ],
 238              'Valid, single trailing / path only' => [
 239                  'issuer_url' => 'https://app.example.com/',
 240                  'http_response' => new Response(
 241                      200,
 242                      ['Content-Type' => 'application/json'],
 243                      json_encode([
 244                          "issuer" => "https://app.example.com",
 245                          "authorization_endpoint" => "https://app.example.com/authorize",
 246                          "token_endpoint" => "https://app.example.com/token",
 247                          "token_endpoint_auth_methods_supported" => [
 248                              "client_secret_basic",
 249                              "private_key_jwt"
 250                          ],
 251                          "token_endpoint_auth_signing_alg_values_supported" => [
 252                              "RS256",
 253                              "ES256"
 254                          ],
 255                          "userinfo_endpoint" => "https://app.example.com/userinfo",
 256                          "jwks_uri" => "https://app.example.com/jwks.json",
 257                          "registration_endpoint" => "https://app.example.com/register",
 258                          "scopes_supported" => [
 259                              "openid",
 260                              "profile",
 261                              "email",
 262                          ],
 263                          "response_types_supported" => [
 264                              "code",
 265                              "code token"
 266                          ],
 267                          "service_documentation" => "http://app.example.com/service_documentation.html",
 268                          "ui_locales_supported" => [
 269                              "en-US",
 270                              "en-GB",
 271                              "fr-FR",
 272                          ]
 273                      ])
 274                  ),
 275                  'well_known_suffix' => null,
 276                  'expected' => [
 277                      'request' => [
 278                          'url' => 'https://app.example.com/.well-known/oauth-authorization-server'
 279                      ],
 280                      'metadata' => [
 281                          "issuer" => "https://app.example.com",
 282                          "authorization_endpoint" => "https://app.example.com/authorize",
 283                          "token_endpoint" => "https://app.example.com/token",
 284                          "token_endpoint_auth_methods_supported" => [
 285                              "client_secret_basic",
 286                              "private_key_jwt"
 287                          ],
 288                          "token_endpoint_auth_signing_alg_values_supported" => [
 289                              "RS256",
 290                              "ES256"
 291                          ],
 292                          "userinfo_endpoint" => "https://app.example.com/userinfo",
 293                          "jwks_uri" => "https://app.example.com/jwks.json",
 294                          "registration_endpoint" => "https://app.example.com/register",
 295                          "scopes_supported" => [
 296                              "openid",
 297                              "profile",
 298                              "email",
 299                          ],
 300                          "response_types_supported" => [
 301                              "code",
 302                              "code token"
 303                          ],
 304                          "service_documentation" => "http://app.example.com/service_documentation.html",
 305                          "ui_locales_supported" => [
 306                              "en-US",
 307                              "en-GB",
 308                              "fr-FR",
 309                          ]
 310                      ]
 311                  ]
 312              ],
 313              'Invalid, non HTTPS issuer URL' => [
 314                  'issuer_url' => 'http://app.example.com',
 315                  'http_response' => new Response(
 316                      200,
 317                      ['Content-Type' => 'application/json'],
 318                      json_encode([
 319                          "issuer" => "https://app.example.com",
 320                          "authorization_endpoint" => "https://app.example.com/authorize",
 321                          "token_endpoint" => "https://app.example.com/token",
 322                          "token_endpoint_auth_methods_supported" => [
 323                              "client_secret_basic",
 324                              "private_key_jwt"
 325                          ],
 326                          "token_endpoint_auth_signing_alg_values_supported" => [
 327                              "RS256",
 328                              "ES256"
 329                          ],
 330                          "userinfo_endpoint" => "https://app.example.com/userinfo",
 331                          "jwks_uri" => "https://app.example.com/jwks.json",
 332                          "registration_endpoint" => "https://app.example.com/register",
 333                          "scopes_supported" => [
 334                              "openid",
 335                              "profile",
 336                              "email",
 337                          ],
 338                          "response_types_supported" => [
 339                              "code",
 340                              "code token"
 341                          ],
 342                          "service_documentation" => "http://app.example.com/service_documentation.html",
 343                          "ui_locales_supported" => [
 344                              "en-US",
 345                              "en-GB",
 346                              "fr-FR",
 347                          ]
 348                      ])
 349                  ),
 350                  'well_known_suffix' => null,
 351                  'expected' => [
 352                      'exception' => \moodle_exception::class
 353                  ]
 354              ],
 355              'Invalid, query string in issuer URL' => [
 356                  'issuer_url' => 'https://app.example.com?test=cat',
 357                  'http_response' => new Response(
 358                      200,
 359                      ['Content-Type' => 'application/json'],
 360                      json_encode([
 361                          "issuer" => "https://app.example.com",
 362                          "authorization_endpoint" => "https://app.example.com/authorize",
 363                          "token_endpoint" => "https://app.example.com/token",
 364                          "token_endpoint_auth_methods_supported" => [
 365                              "client_secret_basic",
 366                              "private_key_jwt"
 367                          ],
 368                          "token_endpoint_auth_signing_alg_values_supported" => [
 369                              "RS256",
 370                              "ES256"
 371                          ],
 372                          "userinfo_endpoint" => "https://app.example.com/userinfo",
 373                          "jwks_uri" => "https://app.example.com/jwks.json",
 374                          "registration_endpoint" => "https://app.example.com/register",
 375                          "scopes_supported" => [
 376                              "openid",
 377                              "profile",
 378                              "email",
 379                          ],
 380                          "response_types_supported" => [
 381                              "code",
 382                              "code token"
 383                          ],
 384                          "service_documentation" => "http://app.example.com/service_documentation.html",
 385                          "ui_locales_supported" => [
 386                              "en-US",
 387                              "en-GB",
 388                              "fr-FR",
 389                          ]
 390                      ])
 391                  ),
 392                  'well_known_suffix' => null,
 393                  'expected' => [
 394                      'exception' => \moodle_exception::class
 395                  ]
 396              ],
 397              'Invalid, fragment in issuer URL' => [
 398                  'issuer_url' => 'https://app.example.com/#cat',
 399                  'http_response' => new Response(
 400                      200,
 401                      ['Content-Type' => 'application/json'],
 402                      json_encode([
 403                          "issuer" => "https://app.example.com",
 404                          "authorization_endpoint" => "https://app.example.com/authorize",
 405                          "token_endpoint" => "https://app.example.com/token",
 406                          "token_endpoint_auth_methods_supported" => [
 407                              "client_secret_basic",
 408                              "private_key_jwt"
 409                          ],
 410                          "token_endpoint_auth_signing_alg_values_supported" => [
 411                              "RS256",
 412                              "ES256"
 413                          ],
 414                          "userinfo_endpoint" => "https://app.example.com/userinfo",
 415                          "jwks_uri" => "https://app.example.com/jwks.json",
 416                          "registration_endpoint" => "https://app.example.com/register",
 417                          "scopes_supported" => [
 418                              "openid",
 419                              "profile",
 420                              "email",
 421                          ],
 422                          "response_types_supported" => [
 423                              "code",
 424                              "code token"
 425                          ],
 426                          "service_documentation" => "http://app.example.com/service_documentation.html",
 427                          "ui_locales_supported" => [
 428                              "en-US",
 429                              "en-GB",
 430                              "fr-FR",
 431                          ]
 432                      ])
 433                  ),
 434                  'well_known_suffix' => null,
 435                  'expected' => [
 436                      'exception' => \moodle_exception::class
 437                  ]
 438              ],
 439              'Valid, port in issuer URL' => [
 440                  'issuer_url' => 'https://app.example.com:8080/some/path',
 441                  'http_response' => new Response(
 442                      200,
 443                      ['Content-Type' => 'application/json'],
 444                      json_encode([
 445                          "issuer" => "https://app.example.com",
 446                          "authorization_endpoint" => "https://app.example.com/authorize",
 447                          "token_endpoint" => "https://app.example.com/token",
 448                          "token_endpoint_auth_methods_supported" => [
 449                              "client_secret_basic",
 450                              "private_key_jwt"
 451                          ],
 452                          "token_endpoint_auth_signing_alg_values_supported" => [
 453                              "RS256",
 454                              "ES256"
 455                          ],
 456                          "userinfo_endpoint" => "https://app.example.com/userinfo",
 457                          "jwks_uri" => "https://app.example.com/jwks.json",
 458                          "registration_endpoint" => "https://app.example.com/register",
 459                          "scopes_supported" => [
 460                              "openid",
 461                              "profile",
 462                              "email",
 463                          ],
 464                          "response_types_supported" => [
 465                              "code",
 466                              "code token"
 467                          ],
 468                          "service_documentation" => "http://app.example.com/service_documentation.html",
 469                          "ui_locales_supported" => [
 470                              "en-US",
 471                              "en-GB",
 472                              "fr-FR",
 473                          ]
 474                      ])
 475                  ),
 476                  'well_known_suffix' => null,
 477                  'expected' => [
 478                      'request' => [
 479                          'url' => 'https://app.example.com:8080/.well-known/oauth-authorization-server/some/path'
 480                      ],
 481                      'metadata' => [
 482                          "issuer" => "https://app.example.com",
 483                          "authorization_endpoint" => "https://app.example.com/authorize",
 484                          "token_endpoint" => "https://app.example.com/token",
 485                          "token_endpoint_auth_methods_supported" => [
 486                              "client_secret_basic",
 487                              "private_key_jwt"
 488                          ],
 489                          "token_endpoint_auth_signing_alg_values_supported" => [
 490                              "RS256",
 491                              "ES256"
 492                          ],
 493                          "userinfo_endpoint" => "https://app.example.com/userinfo",
 494                          "jwks_uri" => "https://app.example.com/jwks.json",
 495                          "registration_endpoint" => "https://app.example.com/register",
 496                          "scopes_supported" => [
 497                              "openid",
 498                              "profile",
 499                              "email",
 500                          ],
 501                          "response_types_supported" => [
 502                              "code",
 503                              "code token"
 504                          ],
 505                          "service_documentation" => "http://app.example.com/service_documentation.html",
 506                          "ui_locales_supported" => [
 507                              "en-US",
 508                              "en-GB",
 509                              "fr-FR",
 510                          ]
 511                      ]
 512                  ]
 513              ],
 514              'Valid, alternate well known suffix, no path' => [
 515                  'issuer_url' => 'https://app.example.com',
 516                  'http_response' => new Response(
 517                      200,
 518                      ['Content-Type' => 'application/json'],
 519                      json_encode([
 520                          "issuer" => "https://app.example.com",
 521                          "authorization_endpoint" => "https://app.example.com/authorize",
 522                          "token_endpoint" => "https://app.example.com/token",
 523                          "token_endpoint_auth_methods_supported" => [
 524                              "client_secret_basic",
 525                              "private_key_jwt"
 526                          ],
 527                          "token_endpoint_auth_signing_alg_values_supported" => [
 528                              "RS256",
 529                              "ES256"
 530                          ],
 531                          "userinfo_endpoint" => "https://app.example.com/userinfo",
 532                          "jwks_uri" => "https://app.example.com/jwks.json",
 533                          "registration_endpoint" => "https://app.example.com/register",
 534                          "scopes_supported" => [
 535                              "openid",
 536                              "profile",
 537                              "email",
 538                          ],
 539                          "response_types_supported" => [
 540                              "code",
 541                              "code token"
 542                          ],
 543                          "service_documentation" => "http://app.example.com/service_documentation.html",
 544                          "ui_locales_supported" => [
 545                              "en-US",
 546                              "en-GB",
 547                              "fr-FR",
 548                          ]
 549                      ])
 550                  ),
 551                  'well_known_suffix' => 'openid-configuration', // An application using the openid well known, which is valid.
 552                  'expected' => [
 553                      'request' => [
 554                          'url' => 'https://app.example.com/.well-known/openid-configuration'
 555                      ],
 556                      'metadata' => [
 557                          "issuer" => "https://app.example.com",
 558                          "authorization_endpoint" => "https://app.example.com/authorize",
 559                          "token_endpoint" => "https://app.example.com/token",
 560                          "token_endpoint_auth_methods_supported" => [
 561                              "client_secret_basic",
 562                              "private_key_jwt"
 563                          ],
 564                          "token_endpoint_auth_signing_alg_values_supported" => [
 565                              "RS256",
 566                              "ES256"
 567                          ],
 568                          "userinfo_endpoint" => "https://app.example.com/userinfo",
 569                          "jwks_uri" => "https://app.example.com/jwks.json",
 570                          "registration_endpoint" => "https://app.example.com/register",
 571                          "scopes_supported" => [
 572                              "openid",
 573                              "profile",
 574                              "email",
 575                          ],
 576                          "response_types_supported" => [
 577                              "code",
 578                              "code token"
 579                          ],
 580                          "service_documentation" => "http://app.example.com/service_documentation.html",
 581                          "ui_locales_supported" => [
 582                              "en-US",
 583                              "en-GB",
 584                              "fr-FR",
 585                          ]
 586                      ]
 587                  ]
 588              ],
 589              'Valid, alternate well known suffix, with path' => [
 590                  'issuer_url' => 'https://app.example.com/some/path/',
 591                  'http_response' => new Response(
 592                      200,
 593                      ['Content-Type' => 'application/json'],
 594                      json_encode([
 595                          "issuer" => "https://app.example.com",
 596                          "authorization_endpoint" => "https://app.example.com/authorize",
 597                          "token_endpoint" => "https://app.example.com/token",
 598                          "token_endpoint_auth_methods_supported" => [
 599                              "client_secret_basic",
 600                              "private_key_jwt"
 601                          ],
 602                          "token_endpoint_auth_signing_alg_values_supported" => [
 603                              "RS256",
 604                              "ES256"
 605                          ],
 606                          "userinfo_endpoint" => "https://app.example.com/userinfo",
 607                          "jwks_uri" => "https://app.example.com/jwks.json",
 608                          "registration_endpoint" => "https://app.example.com/register",
 609                          "scopes_supported" => [
 610                              "openid",
 611                              "profile",
 612                              "email",
 613                          ],
 614                          "response_types_supported" => [
 615                              "code",
 616                              "code token"
 617                          ],
 618                          "service_documentation" => "http://app.example.com/service_documentation.html",
 619                          "ui_locales_supported" => [
 620                              "en-US",
 621                              "en-GB",
 622                              "fr-FR",
 623                          ]
 624                      ])
 625                  ),
 626                  'well_known_suffix' => 'openid-configuration', // An application using the openid well known, which is valid.
 627                  'expected' => [
 628                      'request' => [
 629                          'url' => 'https://app.example.com/.well-known/openid-configuration/some/path/'
 630                      ],
 631                      'metadata' => [
 632                          "issuer" => "https://app.example.com",
 633                          "authorization_endpoint" => "https://app.example.com/authorize",
 634                          "token_endpoint" => "https://app.example.com/token",
 635                          "token_endpoint_auth_methods_supported" => [
 636                              "client_secret_basic",
 637                              "private_key_jwt"
 638                          ],
 639                          "token_endpoint_auth_signing_alg_values_supported" => [
 640                              "RS256",
 641                              "ES256"
 642                          ],
 643                          "userinfo_endpoint" => "https://app.example.com/userinfo",
 644                          "jwks_uri" => "https://app.example.com/jwks.json",
 645                          "registration_endpoint" => "https://app.example.com/register",
 646                          "scopes_supported" => [
 647                              "openid",
 648                              "profile",
 649                              "email",
 650                          ],
 651                          "response_types_supported" => [
 652                              "code",
 653                              "code token"
 654                          ],
 655                          "service_documentation" => "http://app.example.com/service_documentation.html",
 656                          "ui_locales_supported" => [
 657                              "en-US",
 658                              "en-GB",
 659                              "fr-FR",
 660                          ]
 661                      ]
 662                  ]
 663              ],
 664              'Invalid, bad response' => [
 665                  'issuer_url' => 'https://app.example.com',
 666                  'http_response' => new Response(404),
 667                  'well_known_suffix' => null,
 668                  'expected' => [
 669                      'exception' => ClientException::class
 670                  ]
 671              ]
 672          ];
 673      }
 674  }