feat(auth): harden login flows and finalize quality-check coverage

This commit is contained in:
2026-03-07 22:54:33 +01:00
parent aefe80457c
commit 7153fec05c
20 changed files with 1769 additions and 28 deletions

View File

@@ -397,6 +397,83 @@ class MicrosoftOidcServiceTest extends TestCase
$this->assertSame('tenant-id-123', (string) (($result['claims'] ?? [])['tid'] ?? ''));
}
public function testHandleCallbackReturnsEmailDomainNotAllowedWhenTenantDomainPolicyRejectsUser(): void
{
[$idToken, $jwk] = $this->buildValidIdToken('client-id', 'tenant-id-123', 'nonce-123');
$stateStore = $this->createMock(MicrosoftOidcStateStoreService::class);
$stateStore->method('consume')->willReturn([
'ok' => true,
'entry' => [
'tenant_id' => 12,
'nonce' => 'nonce-123',
'code_verifier' => 'verifier-123',
'redirect_uri' => 'https://app.example/auth/microsoft/callback',
'locale' => 'de',
],
]);
$tenantGateway = $this->createMock(AuthTenantGateway::class);
$tenantGateway->expects($this->once())
->method('findById')
->with(12)
->willReturn([
'id' => 12,
'status' => 'active',
'uuid' => 'tenant-uuid',
]);
$tenantSsoService = $this->createMock(TenantSsoService::class);
$tenantSsoService->method('getEffectiveMicrosoftProviderConfig')->willReturn([
'ok' => true,
'config' => [
'authority' => 'https://login.microsoftonline.com/tenant-id-123/v2.0',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'tenant_id' => 'tenant-id-123',
'allowed_domains' => 'allowed.example.com',
'sync_profile_on_login' => 0,
'sync_profile_fields' => [],
],
]);
$tenantSsoService->method('normalizeProfileSyncFields')->willReturn([]);
$tenantSsoService->expects($this->once())
->method('isEmailDomainAllowed')
->with('user@example.com', ['allowed.example.com'])
->willReturn(false);
$oidcGateway = $this->createMock(OidcHttpGateway::class);
$oidcGateway->expects($this->exactly(3))
->method('call')
->willReturnOnConsecutiveCalls(
[
'status' => 200,
'data' => json_encode([
'token_endpoint' => 'https://login.microsoftonline.com/tenant-id-123/oauth2/v2.0/token',
'issuer' => 'https://login.microsoftonline.com/{tenantid}/v2.0',
'jwks_uri' => 'https://login.microsoftonline.com/tenant-id-123/discovery/v2.0/keys',
]) ?: '{}',
],
[
'status' => 200,
'data' => json_encode([
'id_token' => $idToken,
'access_token' => '',
]) ?: '{}',
],
[
'status' => 200,
'data' => json_encode(['keys' => [$jwk]]) ?: '{}',
],
);
$service = $this->newService($tenantSsoService, $tenantGateway, $oidcGateway, $stateStore);
$result = $service->handleCallback('state-ok', 'code-ok');
$this->assertFalse($result['ok']);
$this->assertSame('email_domain_not_allowed', $result['error']);
}
private function newServiceForTokenFlow(array $discoveryResponse, ?array $tokenResponse = null): MicrosoftOidcService
{
$stateStore = $this->createMock(MicrosoftOidcStateStoreService::class);