passbolt/passbolt_api · error · InternalErrorException
Tenant ID should be a valid UUID.
Error message
Tenant ID should be a valid UUID.
What it means
Thrown by SmtpOauthExchangeOnlineService::assertConfiguration (invoked from the constructor) when the `tenant_id` in the SMTP OAuth2 configuration for Microsoft Exchange Online is not a valid UUID. Since the service validates config at construction time, any use of the service with bad config fails immediately with a 500 InternalError.
Solutions
- Copy the correct Directory (tenant) ID GUID from Azure Portal > Microsoft Entra ID > Overview into the SMTP settings.
- Re-save the SMTP OAuth settings via the SMTP settings API so validation runs on write.
- Verify the stored config value is a UUID: 8-4-4-4-12 hex format.
Example fix
// before 'tenant_id' => 'contoso.onmicrosoft.com' // after 'tenant_id' => 'b4e1a1c2-9f3e-4a7d-8c5b-2f6d0e1a9b3c'
Defensive patterns
Strategy: validation
Validate before calling
use Cake\Validation\Validation;
if (!Validation::uuid($config['tenant_id'] ?? '')) {
throw new InvalidArgumentException('tenant_id must be a UUID');
} Type guard
function isTenantIdValid(mixed $tenantId): bool {
return is_string($tenantId) && Cake\Validation\Validation::uuid($tenantId);
} Try / catch
try {
$service = new SmtpOauthExchangeOnlineService($config);
} catch (InternalErrorException $e) {
// surface a config error: tenant_id/client_id is not a UUID
} Prevention
- Copy the Directory (tenant) ID GUID, not the tenant domain, from Azure Portal.
- Validate UUID format before saving OAuth SMTP settings via the API.
- Never insert OAuth config directly into the database.
- Keep a checklist when setting up Exchange OAuth (tenant id, client id, secret, consent).
When it happens
Trigger: Constructing SmtpOauthExchangeOnlineService with smtpSettings OAuth config whose tenant_id is empty, a GUID with wrong format, a domain name (e.g. contoso.onmicrosoft.com), or arbitrary text instead of the Azure AD Directory (tenant) ID GUID.
Common situations: Admins copied the tenant domain instead of the Directory ID from Azure Portal; OAuth settings saved before the tenant_id field was filled; manual DB edits to smtp_settings OAuth payload.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- Client ID should be a valid UUID.
- Failed to obtain SMTP OAuth2 access token.
- SMTP OAuth2 token response from Microsoft did not contain…
- Could not validate the smtp settings.
- Could not validate the smtp settings found in database.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/886213b93611fe47.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltCe/SmtpSettings/src/Service/SmtpOauthExchangeOnlineService.php:95
$this->tenantId = $config['tenant_id'];
$this->clientId = $config['client_id'];
$this->clientSecret = $config['client_secret'];
$this->username = $config['oauth_username'];
// default timeout is 30 (same) but added here for more visibility
$this->httpClient = $httpClient ?? new Client(['timeout' => 30]);
}
/**
* Add basic data validation check to reduce SSRF risk.
* We are not using form class as it can create overhead in this scenario.
*
* @param array $config Configuration to check.
* @return void
*/
private function assertConfiguration(array $config): void
{
if (!Validation::uuid($config['tenant_id'])) {
throw new InternalErrorException(__('Tenant ID should be a valid UUID.'));
}
if (!Validation::uuid($config['client_id'])) {
throw new InternalErrorException(__('Client ID should be a valid UUID.'));
}
}
/**
* Fetch an OAuth2 access token from Microsoft using client credentials grant.
*
* @return string The access token.
* @throws \Cake\Http\Exception\InternalErrorException If the token request fails.
* @see https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-client-creds-grant-flow#get-a-token
*/
public function getAccessToken(): string
{
$tokenUrl = str_replace('__TENANT_ID__', $this->tenantId, self::LOGIN_TOKEN_URL);
$response = $this->httpClient->post($tokenUrl, [View on GitHub (pinned to 31c1bbc10f)