connection_type !== ProviderConnectionType::Dedicated) { throw new InvalidArgumentException('Dedicated provider credentials are only available for dedicated connections.'); } $credential = $connection->credential; if (! $credential instanceof ProviderCredential) { throw new RuntimeException('Provider credentials are missing.'); } if ($credential->type !== 'client_secret') { throw new RuntimeException('Unsupported provider credential type.'); } $payload = $credential->payload; if (! is_array($payload)) { throw new RuntimeException('Provider credential payload is invalid.'); } $clientId = trim((string) ($payload['client_id'] ?? '')); $clientSecret = trim((string) ($payload['client_secret'] ?? '')); if ($clientId === '' || $clientSecret === '') { throw new RuntimeException('Provider credential payload is missing required keys.'); } $tenantId = $payload['tenant_id'] ?? null; if (is_string($tenantId) && $tenantId !== '' && $tenantId !== $connection->entra_tenant_id) { throw new InvalidArgumentException('Provider credential tenant_id does not match the connection entra_tenant_id.'); } return [ 'client_id' => $clientId, 'client_secret' => $clientSecret, ]; } public function upsertClientSecretCredential( ProviderConnection $connection, string $clientId, string $clientSecret, ProviderCredentialSource $source = ProviderCredentialSource::DedicatedManual, ): ProviderCredential { $clientId = trim($clientId); $clientSecret = trim($clientSecret); if ($clientId === '' || $clientSecret === '') { throw new InvalidArgumentException('client_id and client_secret are required.'); } $existing = $connection->credential; $existingPayload = $existing instanceof ProviderCredential && is_array($existing->payload) ? $existing->payload : []; $secretChanged = ! $existing instanceof ProviderCredential || trim((string) ($existingPayload['client_secret'] ?? '')) !== $clientSecret; return ProviderCredential::query()->updateOrCreate( [ 'provider_connection_id' => $connection->getKey(), ], [ 'type' => 'client_secret', 'credential_kind' => ProviderCredentialKind::ClientSecret->value, 'source' => $source->value, 'last_rotated_at' => $secretChanged ? now() : $existing?->last_rotated_at, 'expires_at' => $existing?->expires_at, 'payload' => [ 'client_id' => $clientId, 'client_secret' => $clientSecret, ], ], ); } public function updateClientIdPreservingSecret(ProviderConnection $connection, string $clientId): ProviderCredential { $clientId = trim($clientId); if ($clientId === '') { throw new InvalidArgumentException('client_id is required.'); } $existing = $this->getClientCredentials($connection); $source = $connection->credential?->source instanceof ProviderCredentialSource ? $connection->credential->source : ProviderCredentialSource::DedicatedManual; return $this->upsertClientSecretCredential( connection: $connection, clientId: $clientId, clientSecret: (string) $existing['client_secret'], source: $source, ); } }