| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace Tests\Auth; | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | use BookStack\Actions\ActivityType; | 
					
						
							|  |  |  | use BookStack\Auth\User; | 
					
						
							|  |  |  | use GuzzleHttp\Psr7\Request; | 
					
						
							|  |  |  | use GuzzleHttp\Psr7\Response; | 
					
						
							|  |  |  | use Tests\Helpers\OidcJwtHelper; | 
					
						
							|  |  |  | use Tests\TestCase; | 
					
						
							|  |  |  | use Tests\TestResponse; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class OidcTest extends TestCase | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-02-24 22:16:09 +08:00
										 |  |  |     protected string $keyFilePath; | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |     protected $keyFile; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-31 04:29:59 +08:00
										 |  |  |     protected function setUp(): void | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         parent::setUp(); | 
					
						
							|  |  |  |         // Set default config for OpenID Connect
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->keyFile = tmpfile(); | 
					
						
							|  |  |  |         $this->keyFilePath = 'file://' . stream_get_meta_data($this->keyFile)['uri']; | 
					
						
							|  |  |  |         file_put_contents($this->keyFilePath, OidcJwtHelper::publicPemKey()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         config()->set([ | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'auth.method'                 => 'oidc', | 
					
						
							|  |  |  |             'auth.defaults.guard'         => 'oidc', | 
					
						
							|  |  |  |             'oidc.name'                   => 'SingleSignOn-Testing', | 
					
						
							|  |  |  |             'oidc.display_name_claims'    => ['name'], | 
					
						
							|  |  |  |             'oidc.client_id'              => OidcJwtHelper::defaultClientId(), | 
					
						
							|  |  |  |             'oidc.client_secret'          => 'testpass', | 
					
						
							|  |  |  |             'oidc.jwt_public_key'         => $this->keyFilePath, | 
					
						
							|  |  |  |             'oidc.issuer'                 => OidcJwtHelper::defaultIssuer(), | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |             'oidc.authorization_endpoint' => 'https://oidc.local/auth', | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'oidc.token_endpoint'         => 'https://oidc.local/token', | 
					
						
							|  |  |  |             'oidc.discover'               => false, | 
					
						
							|  |  |  |             'oidc.dump_user_details'      => false, | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |         ]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-31 04:29:59 +08:00
										 |  |  |     protected function tearDown(): void | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         parent::tearDown(); | 
					
						
							|  |  |  |         if (file_exists($this->keyFilePath)) { | 
					
						
							|  |  |  |             unlink($this->keyFilePath); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function test_login_option_shows_on_login_page() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $req = $this->get('/login'); | 
					
						
							|  |  |  |         $req->assertSeeText('SingleSignOn-Testing'); | 
					
						
							|  |  |  |         $req->assertElementExists('form[action$="/oidc/login"][method=POST] button'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function test_oidc_routes_are_only_active_if_oidc_enabled() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         config()->set(['auth.method' => 'standard']); | 
					
						
							|  |  |  |         $routes = ['/login' => 'post', '/callback' => 'get']; | 
					
						
							|  |  |  |         foreach ($routes as $uri => $method) { | 
					
						
							|  |  |  |             $req = $this->call($method, '/oidc' . $uri); | 
					
						
							|  |  |  |             $this->assertPermissionError($req); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function test_forgot_password_routes_inaccessible() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $resp = $this->get('/password/email'); | 
					
						
							|  |  |  |         $this->assertPermissionError($resp); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $resp = $this->post('/password/email'); | 
					
						
							|  |  |  |         $this->assertPermissionError($resp); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $resp = $this->get('/password/reset/abc123'); | 
					
						
							|  |  |  |         $this->assertPermissionError($resp); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $resp = $this->post('/password/reset'); | 
					
						
							|  |  |  |         $this->assertPermissionError($resp); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function test_standard_login_routes_inaccessible() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $resp = $this->post('/login'); | 
					
						
							|  |  |  |         $this->assertPermissionError($resp); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function test_logout_route_functions() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->actingAs($this->getEditor()); | 
					
						
							| 
									
										
										
										
											2021-11-15 05:13:24 +08:00
										 |  |  |         $this->post('/logout'); | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |         $this->assertFalse(auth()->check()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function test_user_invite_routes_inaccessible() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $resp = $this->get('/register/invite/abc123'); | 
					
						
							|  |  |  |         $this->assertPermissionError($resp); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $resp = $this->post('/register/invite/abc123'); | 
					
						
							|  |  |  |         $this->assertPermissionError($resp); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function test_user_register_routes_inaccessible() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $resp = $this->get('/register'); | 
					
						
							|  |  |  |         $this->assertPermissionError($resp); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $resp = $this->post('/register'); | 
					
						
							|  |  |  |         $this->assertPermissionError($resp); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function test_login() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $req = $this->post('/oidc/login'); | 
					
						
							|  |  |  |         $redirect = $req->headers->get('location'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->assertStringStartsWith('https://oidc.local/auth', $redirect, 'Login redirects to SSO location'); | 
					
						
							|  |  |  |         $this->assertFalse($this->isAuthenticated()); | 
					
						
							|  |  |  |         $this->assertStringContainsString('scope=openid%20profile%20email', $redirect); | 
					
						
							|  |  |  |         $this->assertStringContainsString('client_id=' . OidcJwtHelper::defaultClientId(), $redirect); | 
					
						
							|  |  |  |         $this->assertStringContainsString('redirect_uri=' . urlencode(url('/oidc/callback')), $redirect); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function test_login_success_flow() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // Start auth
 | 
					
						
							|  |  |  |         $this->post('/oidc/login'); | 
					
						
							|  |  |  |         $state = session()->get('oidc_state'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $transactions = &$this->mockHttpClient([$this->getMockAuthorizationResponse([ | 
					
						
							|  |  |  |             'email' => 'benny@example.com', | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'sub'   => 'benny1010101', | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |         ])]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Callback from auth provider
 | 
					
						
							|  |  |  |         // App calls token endpoint to get id token
 | 
					
						
							|  |  |  |         $resp = $this->get('/oidc/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=' . $state); | 
					
						
							|  |  |  |         $resp->assertRedirect('/'); | 
					
						
							|  |  |  |         $this->assertCount(1, $transactions); | 
					
						
							|  |  |  |         /** @var Request $tokenRequest */ | 
					
						
							|  |  |  |         $tokenRequest = $transactions[0]['request']; | 
					
						
							|  |  |  |         $this->assertEquals('https://oidc.local/token', (string) $tokenRequest->getUri()); | 
					
						
							|  |  |  |         $this->assertEquals('POST', $tokenRequest->getMethod()); | 
					
						
							|  |  |  |         $this->assertEquals('Basic ' . base64_encode(OidcJwtHelper::defaultClientId() . ':testpass'), $tokenRequest->getHeader('Authorization')[0]); | 
					
						
							|  |  |  |         $this->assertStringContainsString('grant_type=authorization_code', $tokenRequest->getBody()); | 
					
						
							|  |  |  |         $this->assertStringContainsString('code=SplxlOBeZQQYbYS6WxSbIA', $tokenRequest->getBody()); | 
					
						
							|  |  |  |         $this->assertStringContainsString('redirect_uri=' . urlencode(url('/oidc/callback')), $tokenRequest->getBody()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->assertTrue(auth()->check()); | 
					
						
							|  |  |  |         $this->assertDatabaseHas('users', [ | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'email'            => 'benny@example.com', | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |             'external_auth_id' => 'benny1010101', | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'email_confirmed'  => false, | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |         ]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $user = User::query()->where('email', '=', 'benny@example.com')->first(); | 
					
						
							|  |  |  |         $this->assertActivityExists(ActivityType::AUTH_LOGIN, null, "oidc; ({$user->id}) Barry Scott"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function test_callback_fails_if_no_state_present_or_matching() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->get('/oidc/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=abc124'); | 
					
						
							|  |  |  |         $this->assertSessionError('Login using SingleSignOn-Testing failed, system did not provide successful authorization'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->post('/oidc/login'); | 
					
						
							|  |  |  |         $this->get('/oidc/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=abc124'); | 
					
						
							|  |  |  |         $this->assertSessionError('Login using SingleSignOn-Testing failed, system did not provide successful authorization'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function test_dump_user_details_option_outputs_as_expected() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         config()->set('oidc.dump_user_details', true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $resp = $this->runLogin([ | 
					
						
							|  |  |  |             'email' => 'benny@example.com', | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'sub'   => 'benny505', | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |         ]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $resp->assertStatus(200); | 
					
						
							|  |  |  |         $resp->assertJson([ | 
					
						
							|  |  |  |             'email' => 'benny@example.com', | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'sub'   => 'benny505', | 
					
						
							|  |  |  |             'iss'   => OidcJwtHelper::defaultIssuer(), | 
					
						
							|  |  |  |             'aud'   => OidcJwtHelper::defaultClientId(), | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |         ]); | 
					
						
							|  |  |  |         $this->assertFalse(auth()->check()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function test_auth_fails_if_no_email_exists_in_user_data() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->runLogin([ | 
					
						
							|  |  |  |             'email' => '', | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'sub'   => 'benny505', | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |         ]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->assertSessionError('Could not find an email address, for this user, in the data provided by the external authentication system'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function test_auth_fails_if_already_logged_in() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->asEditor(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->runLogin([ | 
					
						
							|  |  |  |             'email' => 'benny@example.com', | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'sub'   => 'benny505', | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |         ]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->assertSessionError('Already logged in'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function test_auth_login_as_existing_user() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $editor = $this->getEditor(); | 
					
						
							|  |  |  |         $editor->external_auth_id = 'benny505'; | 
					
						
							|  |  |  |         $editor->save(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->assertFalse(auth()->check()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->runLogin([ | 
					
						
							|  |  |  |             'email' => 'benny@example.com', | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'sub'   => 'benny505', | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |         ]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->assertTrue(auth()->check()); | 
					
						
							|  |  |  |         $this->assertEquals($editor->id, auth()->user()->id); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function test_auth_login_as_existing_user_email_with_different_auth_id_fails() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $editor = $this->getEditor(); | 
					
						
							|  |  |  |         $editor->external_auth_id = 'editor101'; | 
					
						
							|  |  |  |         $editor->save(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->assertFalse(auth()->check()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-24 22:16:09 +08:00
										 |  |  |         $resp = $this->runLogin([ | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |             'email' => $editor->email, | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'sub'   => 'benny505', | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |         ]); | 
					
						
							| 
									
										
										
										
											2022-02-24 22:16:09 +08:00
										 |  |  |         $resp = $this->followRedirects($resp); | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-24 22:16:09 +08:00
										 |  |  |         $resp->assertSeeText('A user with the email ' . $editor->email . ' already exists but with different credentials.'); | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |         $this->assertFalse(auth()->check()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function test_auth_login_with_invalid_token_fails() | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2022-02-24 22:16:09 +08:00
										 |  |  |         $resp = $this->runLogin([ | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |             'sub' => null, | 
					
						
							|  |  |  |         ]); | 
					
						
							| 
									
										
										
										
											2022-02-24 22:16:09 +08:00
										 |  |  |         $resp = $this->followRedirects($resp); | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-24 22:16:09 +08:00
										 |  |  |         $resp->assertSeeText('ID token validate failed with error: Missing token subject value'); | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |         $this->assertFalse(auth()->check()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function test_auth_login_with_autodiscovery() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->withAutodiscovery(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $transactions = &$this->mockHttpClient([ | 
					
						
							|  |  |  |             $this->getAutoDiscoveryResponse(), | 
					
						
							|  |  |  |             $this->getJwksResponse(), | 
					
						
							|  |  |  |         ]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->assertFalse(auth()->check()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->runLogin(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->assertTrue(auth()->check()); | 
					
						
							|  |  |  |         /** @var Request $discoverRequest */ | 
					
						
							|  |  |  |         $discoverRequest = $transactions[0]['request']; | 
					
						
							|  |  |  |         /** @var Request $discoverRequest */ | 
					
						
							|  |  |  |         $keysRequest = $transactions[1]['request']; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->assertEquals('GET', $keysRequest->getMethod()); | 
					
						
							|  |  |  |         $this->assertEquals('GET', $discoverRequest->getMethod()); | 
					
						
							|  |  |  |         $this->assertEquals(OidcJwtHelper::defaultIssuer() . '/.well-known/openid-configuration', $discoverRequest->getUri()); | 
					
						
							|  |  |  |         $this->assertEquals(OidcJwtHelper::defaultIssuer() . '/oidc/keys', $keysRequest->getUri()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function test_auth_fails_if_autodiscovery_fails() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->withAutodiscovery(); | 
					
						
							|  |  |  |         $this->mockHttpClient([ | 
					
						
							|  |  |  |             new Response(404, [], 'Not found'), | 
					
						
							|  |  |  |         ]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-24 22:16:09 +08:00
										 |  |  |         $resp = $this->followRedirects($this->runLogin()); | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |         $this->assertFalse(auth()->check()); | 
					
						
							| 
									
										
										
										
											2022-02-24 22:16:09 +08:00
										 |  |  |         $resp->assertSeeText('Login using SingleSignOn-Testing failed, system did not provide successful authorization'); | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function test_autodiscovery_calls_are_cached() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->withAutodiscovery(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $transactions = &$this->mockHttpClient([ | 
					
						
							|  |  |  |             $this->getAutoDiscoveryResponse(), | 
					
						
							|  |  |  |             $this->getJwksResponse(), | 
					
						
							|  |  |  |             $this->getAutoDiscoveryResponse([ | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |                 'issuer' => 'https://auto.example.com', | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |             ]), | 
					
						
							|  |  |  |             $this->getJwksResponse(), | 
					
						
							|  |  |  |         ]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Initial run
 | 
					
						
							|  |  |  |         $this->post('/oidc/login'); | 
					
						
							|  |  |  |         $this->assertCount(2, $transactions); | 
					
						
							|  |  |  |         // Second run, hits cache
 | 
					
						
							|  |  |  |         $this->post('/oidc/login'); | 
					
						
							|  |  |  |         $this->assertCount(2, $transactions); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Third run, different issuer, new cache key
 | 
					
						
							|  |  |  |         config()->set(['oidc.issuer' => 'https://auto.example.com']); | 
					
						
							|  |  |  |         $this->post('/oidc/login'); | 
					
						
							|  |  |  |         $this->assertCount(4, $transactions); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-28 22:00:55 +08:00
										 |  |  |     public function test_auth_login_with_autodiscovery_with_keys_that_do_not_have_alg_property() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->withAutodiscovery(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $keyArray = OidcJwtHelper::publicJwkKeyArray(); | 
					
						
							|  |  |  |         unset($keyArray['alg']); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->mockHttpClient([ | 
					
						
							|  |  |  |             $this->getAutoDiscoveryResponse(), | 
					
						
							|  |  |  |             new Response(200, [ | 
					
						
							|  |  |  |                 'Content-Type'  => 'application/json', | 
					
						
							|  |  |  |                 'Cache-Control' => 'no-cache, no-store', | 
					
						
							|  |  |  |                 'Pragma'        => 'no-cache', | 
					
						
							|  |  |  |             ], json_encode([ | 
					
						
							|  |  |  |                 'keys' => [ | 
					
						
							|  |  |  |                     $keyArray, | 
					
						
							|  |  |  |                 ], | 
					
						
							|  |  |  |             ])), | 
					
						
							|  |  |  |         ]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->assertFalse(auth()->check()); | 
					
						
							|  |  |  |         $this->runLogin(); | 
					
						
							|  |  |  |         $this->assertTrue(auth()->check()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |     protected function withAutodiscovery() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         config()->set([ | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'oidc.issuer'                 => OidcJwtHelper::defaultIssuer(), | 
					
						
							|  |  |  |             'oidc.discover'               => true, | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |             'oidc.authorization_endpoint' => null, | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'oidc.token_endpoint'         => null, | 
					
						
							|  |  |  |             'oidc.jwt_public_key'         => null, | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |         ]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     protected function runLogin($claimOverrides = []): TestResponse | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->post('/oidc/login'); | 
					
						
							|  |  |  |         $state = session()->get('oidc_state'); | 
					
						
							|  |  |  |         $this->mockHttpClient([$this->getMockAuthorizationResponse($claimOverrides)]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $this->get('/oidc/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=' . $state); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     protected function getAutoDiscoveryResponse($responseOverrides = []): Response | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return new Response(200, [ | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'Content-Type'  => 'application/json', | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |             'Cache-Control' => 'no-cache, no-store', | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'Pragma'        => 'no-cache', | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |         ], json_encode(array_merge([ | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'token_endpoint'         => OidcJwtHelper::defaultIssuer() . '/oidc/token', | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |             'authorization_endpoint' => OidcJwtHelper::defaultIssuer() . '/oidc/authorize', | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'jwks_uri'               => OidcJwtHelper::defaultIssuer() . '/oidc/keys', | 
					
						
							|  |  |  |             'issuer'                 => OidcJwtHelper::defaultIssuer(), | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |         ], $responseOverrides))); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     protected function getJwksResponse(): Response | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return new Response(200, [ | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'Content-Type'  => 'application/json', | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |             'Cache-Control' => 'no-cache, no-store', | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'Pragma'        => 'no-cache', | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |         ], json_encode([ | 
					
						
							|  |  |  |             'keys' => [ | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |                 OidcJwtHelper::publicJwkKeyArray(), | 
					
						
							|  |  |  |             ], | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |         ])); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     protected function getMockAuthorizationResponse($claimOverrides = []): Response | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return new Response(200, [ | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'Content-Type'  => 'application/json', | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |             'Cache-Control' => 'no-cache, no-store', | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'Pragma'        => 'no-cache', | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |         ], json_encode([ | 
					
						
							|  |  |  |             'access_token' => 'abc123', | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  |             'token_type'   => 'Bearer', | 
					
						
							|  |  |  |             'expires_in'   => 3600, | 
					
						
							|  |  |  |             'id_token'     => OidcJwtHelper::idToken($claimOverrides), | 
					
						
							| 
									
										
										
										
											2021-10-13 23:51:27 +08:00
										 |  |  |         ])); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |