{"record":{"id":"7d3cd14b36e98a46","repo":"nopSolutions/nopCommerce","slug":"admin-configuration-emailaccounts-fields-tenantid","errorCode":null,"errorMessage":"Admin.Configuration.EmailAccounts.Fields.TenantId.Required","messagePattern":"Admin\\.Configuration\\.EmailAccounts\\.Fields\\.TenantId\\.Required","errorType":"exception","errorClass":"NopException","httpStatus":null,"severity":"error","filePath":"src/Libraries/Nop.Services/Messages/SmtpBuilder.cs","lineNumber":96,"sourceCode":"\n        if (authResult.Credential.Token?.IsStale == true)\n            await authResult.Credential.RefreshTokenAsync(CancellationToken.None);\n\n        return new SaslMechanismOAuth2(authResult.Credential.UserId, authResult.Credential.Token.AccessToken);\n    }\n\n    protected virtual async Task<SaslMechanism> GetExchangeCredentialsAsync(EmailAccount emailAccount)\n    {\n        ArgumentNullException.ThrowIfNull(emailAccount);\n\n        if (string.IsNullOrEmpty(emailAccount.ClientId))\n            throw new NopException(await _localizationService.GetResourceAsync(\"Admin.Configuration.EmailAccounts.Fields.ClientId.Required\"));\n\n        if (string.IsNullOrEmpty(emailAccount.ClientSecret))\n            throw new NopException(await _localizationService.GetResourceAsync(\"Admin.Configuration.EmailAccounts.Fields.ClientSecret.Required\"));\n\n        if (string.IsNullOrEmpty(emailAccount.TenantId))\n            throw new NopException(await _localizationService.GetResourceAsync(\"Admin.Configuration.EmailAccounts.Fields.TenantId.Required\"));\n\n        var confidentialClientApplication = ConfidentialClientApplicationBuilder.Create(emailAccount.ClientId)\n            .WithAuthority(string.Format(NopMessageDefaults.MSALTenantPattern, emailAccount.TenantId))\n            .WithClientSecret(emailAccount.ClientSecret)\n            .Build();\n\n        var authToken = await confidentialClientApplication.AcquireTokenForClient(NopMessageDefaults.MSALScopes).ExecuteAsync();\n\n        return new SaslMechanismOAuth2(emailAccount.Email, authToken.AccessToken);\n    }\n\n    #endregion\n\n    #region Methods\n\n    /// <summary>\n    /// Create a new SMTP client for a specific email account\n    /// </summary>","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/nopSolutions/nopCommerce/blob/64bdf2ff08c8b39e65717bcf974fb43dc2ef68f2/src/Libraries/Nop.Services/Messages/SmtpBuilder.cs#L78-L114","documentation":"Thrown by SmtpBuilder.GetExchangeCredentialsAsync when EmailAccount.TenantId is empty for an Exchange/MSAL account. The authority URL is built with string.Format(MSALTenantPattern, tenantId); an empty tenantId would point at the wrong authority, so the method throws NopException with a localized resource message before constructing the client.","triggerScenarios":"An Exchange-configured EmailAccount with ClientId/Secret set but blank TenantId; sending mail triggers GetExchangeCredentialsAsync.","commonSituations":"Admin registered the Azure app but did not copy the Directory (tenant) ID; multi-tenant confusion; redeploy missing tenant config.","solutions":["In admin, set the EmailAccount TenantId to the Azure AD Directory (tenant) ID.","Use 'common' or the specific tenant GUID per the desired token audience.","Confirm ClientId, ClientSecret, and TenantId are all populated.","Verify the tenant matches where the app registration lives."],"exampleFix":"// before - tenant blank\n\n// after - require tenant\nif (string.IsNullOrWhiteSpace(account.TenantId))\n    return Error(\"Exchange OAuth requires the Azure AD TenantId.\");","handlingStrategy":"validation","validationCode":"if (IsExchangeAccount(account) && string.IsNullOrEmpty(account.TenantId))\n    return Error(\"Set the Azure AD TenantId for this email account.\");","typeGuard":"static bool HasExchangeCredentials(EmailAccount a)\n    => !string.IsNullOrEmpty(a.ClientId)\n       && !string.IsNullOrEmpty(a.ClientSecret)\n       && !string.IsNullOrEmpty(a.TenantId);","tryCatchPattern":"try { await smtpBuilder.BuildAsync(account); }\ncatch (NopException ex) when (ex.Message.Contains(\"EmailAccounts.Fields.TenantId.Required\"))\n{ /* surface localized 'set TenantId' message */ }","preventionTips":["Copy the Azure AD Directory (tenant) ID into the account.","Validate all three MSAL fields together when saving.","Ensure the tenant matches the app registration."],"tags":["email","smtp","oauth","exchange","msal","configuration","nopcommerce"],"backgroundTag":null,"analyzedSha":"64bdf2ff08c8b39e65717bcf974fb43dc2ef68f2","analyzedAt":"2026-08-13T21:19:38.062Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}