diff --git a/connector/new.go b/connector/new.go index 892ad2203..ab2a59f40 100644 --- a/connector/new.go +++ b/connector/new.go @@ -148,138 +148,138 @@ func New(provider providers.Provider, params common.ConnectorParams) (connectors } var connectorConstructors = map[providers.Provider]outputConstructorFunc{ // nolint:gochecknoglobals - providers.AWS: wrapper(newAWSConnector), - providers.AcuityScheduling: wrapper(newAcuitySchedulingConnector), - providers.Aha: wrapper(newAhaConnector), - providers.Aircall: wrapper(newAircallConnector), - providers.Amplitude: wrapper(newAmplitudeConnector), - providers.Apollo: wrapper(newApolloConnector), - providers.Asana: wrapper(newAsanaConnector), - providers.Ashby: wrapper(newAshbyConnector), - providers.Atlassian: wrapper(newAtlassianConnector), - providers.Attio: wrapper(newAttioConnector), - providers.Avoma: wrapper(newAvomaConnector), - providers.BigQuery: wrapper(newBigQueryConnector), - providers.Bentley: wrapper(newBentleyConnector), - providers.Bitbucket: wrapper(newBitBucketConnector), - providers.Blackbaud: wrapper(newBlackbaudConnector), - providers.Blueshift: wrapper(newBlueshiftConnector), - providers.Braintree: wrapper(newBraintreeConnector), - providers.Braze: wrapper(newBrazeConnector), - providers.Breakcold: wrapper(newBreakcoldConnector), - providers.Brevo: wrapper(newBrevoConnector), - providers.Calendly: wrapper(newCalendlyConnector), - providers.CallRail: wrapper(newCallRail), - providers.CampaignMonitor: wrapper(newCampaignMonitorConnector), - providers.Capsule: wrapper(newCapsuleConnector), - providers.Chargebee: wrapper(newChargebeeConnector), - providers.ChargeOver: wrapper(newChargeOver), - providers.ChiliPiper: wrapper(newChiliPiperConnector), - providers.Chorus: wrapper(newChorusConnector), - providers.ClariCopilot: wrapper(newClariCopilotConnector), - providers.ClickUp: wrapper(newClickUpConnector), - providers.Close: wrapper(newCloseConnector), - providers.CloudTalk: wrapper(newCloudTalkConnector), - providers.ConstantContact: wrapper(newConstantContactConnector), - providers.Copper: wrapper(newCopperConnector), - providers.CustomerJourneysApp: wrapper(newCustomerJourneysAppConnector), - providers.DevRev: wrapper(newDevRevConnector), - providers.Dixa: wrapper(newDixaConnector), - providers.Docusign: wrapper(newDocusignConnector), - providers.Drift: wrapper(newDriftConnector), - providers.DropboxSign: wrapper(newDropboxSignConnector), - providers.DynamicsBusinessCentral: wrapper(newDynamicsBusinessCentral), - providers.DynamicsCRM: wrapper(newDynamicsCRMConnector), - providers.FastSpring: wrapper(newFastSpringConnector), - providers.Fathom: wrapper(newFathomConnector), - providers.Fireflies: wrapper(newFirefliesConnector), - providers.Flatfile: wrapper(newFlatfileConnector), - providers.FourFour: wrapper(newFourFourConnector), - providers.Freshdesk: wrapper(newFreshdeskConnector), - providers.Front: wrapper(newFrontConnector), - providers.G2: wrapper(newG2Connector), - providers.GetResponse: wrapper(newGetResponseConnector), - providers.GitLab: wrapper(newGitLabConnector), - providers.Github: wrapper(newGithubConnector), - providers.Gong: wrapper(newGongConnector), - providers.Google: wrapper(newGoogleConnector), - providers.GoogleWorkspaceDelegation: wrapper(newGoogleWorkspaceDelegationConnector), - providers.Gorgias: wrapper(newGorgiasConnector), - providers.Granola: wrapper(newGranolaConnector), - providers.Groove: wrapper(newGrooveConnector), - providers.HappyFox: wrapper(newHappyFoxConnector), - providers.HelpScoutMailbox: wrapper(newHelpScoutMailboxConnector), - providers.HeyReach: wrapper(newHeyReachConnector), - providers.HousecallPro: wrapper(newHousecallProConnector), - providers.HighLevelStandard: wrapper(newHighLevelStandardConnector), - providers.HighLevelWhiteLabel: wrapper(newHighLevelWhiteLabelConnector), - providers.Hubspot: wrapper(newHubspotConnector), - providers.Hunter: wrapper(newHunterConnector), - providers.Insightly: wrapper(newInsightlyConnector), - providers.Instantly: wrapper(newInstantlyConnector), - providers.InstantlyAI: wrapper(newInstantlyAIConnector), - providers.Intercom: wrapper(newIntercomConnector), - providers.Iterable: wrapper(newIterableConnector), - providers.Jobber: wrapper(newJobberConnector), - providers.JustCall: wrapper(newJustCallConnector), - providers.KaseyaVSAX: wrapper(newKaseyaVSAXConnector), - providers.Keap: wrapper(newKeapConnector), - providers.Kit: wrapper(newKitConnector), - providers.Klaviyo: wrapper(newKlaviyoConnector), - providers.Lemlist: wrapper(newLemlistConnector), - providers.Lever: wrapper(newLeverConnector), - providers.Linear: wrapper(newLinearConnector), - providers.LinkedIn: wrapper(newLinkedInConnector), - providers.Loxo: wrapper(newLoxoConnector), - providers.Marketo: wrapper(newMarketoConnector), - providers.Microsoft: wrapper(newMicrosoftConnector), - providers.MicrosoftClientCredentials: wrapper(newMicrosoftClientCredentialsConnector), - providers.Mixmax: wrapper(newMixmaxConnector), - providers.Monday: wrapper(newMondayConnector), - providers.Netsuite: wrapper(newNetsuiteConnector), - providers.NetsuiteM2M: wrapper(newNetsuiteM2MConnector), - providers.Nutshell: wrapper(newNutshellConnector), - providers.Okta: wrapper(newOktaConnector), - providers.Outplay: wrapper(newOutplayConnector), - providers.Outreach: wrapper(newOutreachConnector), - providers.Paddle: wrapper(newPaddleConnector), - providers.PhoneBurner: wrapper(newPhoneBurnerConnector), - providers.Pinterest: wrapper(newPinterestConnector), - providers.Pipedrive: wrapper(newPipedriveConnector), - providers.Pipeliner: wrapper(newPipelinerConnector), - providers.Podium: wrapper(newPodiumConnector), - providers.Pylon: wrapper(newPylonConnector), - providers.QuickBooks: wrapper(newQuickbooksConnector), - providers.QuickbooksSandbox: wrapper(newQuickbooksSandboxConnector), - providers.Recurly: wrapper(newRecurlyConnector), - providers.RevenueCat: wrapper(newRevenueCatConnector), - providers.RingCentral: wrapper(newRingCentral), - providers.SageIntacct: wrapper(newSageIntacctConnector), - providers.Salesfinity: wrapper(newSalesfinityConnector), - providers.Salesflare: wrapper(newSalesflareConnector), - providers.Salesforce: wrapper(newSalesforceConnector), - providers.SalesforceJWT: wrapper(newSalesforceJWTConnector), - providers.Salesloft: wrapper(newSalesloftConnector), - providers.Seismic: wrapper(newSeismicConnector), - providers.Sellsy: wrapper(newSellsyConnector), - providers.ServiceNow: wrapper(newServiceNowConnector), - providers.Shopify: wrapper(newShopifyConnector), - providers.Slack: wrapper(newSlackConnector), - providers.Smartlead: wrapper(newSmartleadConnector), - providers.SnapchatAds: wrapper(newSnapchatAdsConnector), - providers.Snowflake: wrapper(newSnowflakeConnector), - providers.SolarWindsServiceDesk: wrapper(newSolarWindsConnector), - providers.Stripe: wrapper(newStripeConnector), - providers.SuperSend: wrapper(newSuperSendConnector), - providers.Talkdesk: wrapper(newTalkdeskConnector), - providers.Teamleader: wrapper(newTeamleaderConnector), - providers.Teamwork: wrapper(newTeamworkConnector), - providers.Webex: wrapper(newWebexConnector), - providers.Xero: wrapper(newXeroConnector), - providers.ZendeskChat: wrapper(newZendeskChatConnector), - providers.ZendeskSupport: wrapper(newZendeskSupportConnector), - providers.Zoho: wrapper(newZohoConnector), - providers.Zoom: wrapper(newZoomConnector), + providers.AWS: wrapper(newAWSConnector), + providers.AcuityScheduling: wrapper(newAcuitySchedulingConnector), + providers.Aha: wrapper(newAhaConnector), + providers.Aircall: wrapper(newAircallConnector), + providers.Amplitude: wrapper(newAmplitudeConnector), + providers.Apollo: wrapper(newApolloConnector), + providers.Asana: wrapper(newAsanaConnector), + providers.Ashby: wrapper(newAshbyConnector), + providers.Atlassian: wrapper(newAtlassianConnector), + providers.Attio: wrapper(newAttioConnector), + providers.Avoma: wrapper(newAvomaConnector), + providers.BigQuery: wrapper(newBigQueryConnector), + providers.Bentley: wrapper(newBentleyConnector), + providers.Bitbucket: wrapper(newBitBucketConnector), + providers.Blackbaud: wrapper(newBlackbaudConnector), + providers.Blueshift: wrapper(newBlueshiftConnector), + providers.Braintree: wrapper(newBraintreeConnector), + providers.Braze: wrapper(newBrazeConnector), + providers.Breakcold: wrapper(newBreakcoldConnector), + providers.Brevo: wrapper(newBrevoConnector), + providers.Calendly: wrapper(newCalendlyConnector), + providers.CallRail: wrapper(newCallRail), + providers.CampaignMonitor: wrapper(newCampaignMonitorConnector), + providers.Capsule: wrapper(newCapsuleConnector), + providers.Chargebee: wrapper(newChargebeeConnector), + providers.ChargeOver: wrapper(newChargeOver), + providers.ChiliPiper: wrapper(newChiliPiperConnector), + providers.Chorus: wrapper(newChorusConnector), + providers.ClariCopilot: wrapper(newClariCopilotConnector), + providers.ClickUp: wrapper(newClickUpConnector), + providers.Close: wrapper(newCloseConnector), + providers.CloudTalk: wrapper(newCloudTalkConnector), + providers.ConstantContact: wrapper(newConstantContactConnector), + providers.Copper: wrapper(newCopperConnector), + providers.CustomerJourneysApp: wrapper(newCustomerJourneysAppConnector), + providers.DevRev: wrapper(newDevRevConnector), + providers.Dixa: wrapper(newDixaConnector), + providers.Docusign: wrapper(newDocusignConnector), + providers.Drift: wrapper(newDriftConnector), + providers.DropboxSign: wrapper(newDropboxSignConnector), + providers.DynamicsBusinessCentral: wrapper(newDynamicsBusinessCentral), + providers.DynamicsCRM: wrapper(newDynamicsCRMConnector), + providers.FastSpring: wrapper(newFastSpringConnector), + providers.Fathom: wrapper(newFathomConnector), + providers.Fireflies: wrapper(newFirefliesConnector), + providers.Flatfile: wrapper(newFlatfileConnector), + providers.FourFour: wrapper(newFourFourConnector), + providers.Freshdesk: wrapper(newFreshdeskConnector), + providers.Front: wrapper(newFrontConnector), + providers.G2: wrapper(newG2Connector), + providers.GetResponse: wrapper(newGetResponseConnector), + providers.GitLab: wrapper(newGitLabConnector), + providers.Github: wrapper(newGithubConnector), + providers.Gong: wrapper(newGongConnector), + providers.Google: wrapper(newGoogleConnector), + providers.GoogleWorkspaceDelegation: wrapper(newGoogleWorkspaceDelegationConnector), + providers.Gorgias: wrapper(newGorgiasConnector), + providers.Granola: wrapper(newGranolaConnector), + providers.Groove: wrapper(newGrooveConnector), + providers.HappyFox: wrapper(newHappyFoxConnector), + providers.HelpScoutMailbox: wrapper(newHelpScoutMailboxConnector), + providers.HeyReach: wrapper(newHeyReachConnector), + providers.HousecallPro: wrapper(newHousecallProConnector), + providers.HighLevelStandard: wrapper(newHighLevelStandardConnector), + providers.HighLevelWhiteLabel: wrapper(newHighLevelWhiteLabelConnector), + providers.Hubspot: wrapper(newHubspotConnector), + providers.Hunter: wrapper(newHunterConnector), + providers.Insightly: wrapper(newInsightlyConnector), + providers.Instantly: wrapper(newInstantlyConnector), + providers.InstantlyAI: wrapper(newInstantlyAIConnector), + providers.Intercom: wrapper(newIntercomConnector), + providers.Iterable: wrapper(newIterableConnector), + providers.Jobber: wrapper(newJobberConnector), + providers.JustCall: wrapper(newJustCallConnector), + providers.KaseyaVSAX: wrapper(newKaseyaVSAXConnector), + providers.Keap: wrapper(newKeapConnector), + providers.Kit: wrapper(newKitConnector), + providers.Klaviyo: wrapper(newKlaviyoConnector), + providers.Lemlist: wrapper(newLemlistConnector), + providers.Lever: wrapper(newLeverConnector), + providers.Linear: wrapper(newLinearConnector), + providers.LinkedIn: wrapper(newLinkedInConnector), + providers.Loxo: wrapper(newLoxoConnector), + providers.Marketo: wrapper(newMarketoConnector), + providers.Microsoft: wrapper(newMicrosoftConnector), + providers.MicrosoftAdminConsent: wrapper(newMicrosoftAdminConsentConnector), + providers.Mixmax: wrapper(newMixmaxConnector), + providers.Monday: wrapper(newMondayConnector), + providers.Netsuite: wrapper(newNetsuiteConnector), + providers.NetsuiteM2M: wrapper(newNetsuiteM2MConnector), + providers.Nutshell: wrapper(newNutshellConnector), + providers.Okta: wrapper(newOktaConnector), + providers.Outplay: wrapper(newOutplayConnector), + providers.Outreach: wrapper(newOutreachConnector), + providers.Paddle: wrapper(newPaddleConnector), + providers.PhoneBurner: wrapper(newPhoneBurnerConnector), + providers.Pinterest: wrapper(newPinterestConnector), + providers.Pipedrive: wrapper(newPipedriveConnector), + providers.Pipeliner: wrapper(newPipelinerConnector), + providers.Podium: wrapper(newPodiumConnector), + providers.Pylon: wrapper(newPylonConnector), + providers.QuickBooks: wrapper(newQuickbooksConnector), + providers.QuickbooksSandbox: wrapper(newQuickbooksSandboxConnector), + providers.Recurly: wrapper(newRecurlyConnector), + providers.RevenueCat: wrapper(newRevenueCatConnector), + providers.RingCentral: wrapper(newRingCentral), + providers.SageIntacct: wrapper(newSageIntacctConnector), + providers.Salesfinity: wrapper(newSalesfinityConnector), + providers.Salesflare: wrapper(newSalesflareConnector), + providers.Salesforce: wrapper(newSalesforceConnector), + providers.SalesforceJWT: wrapper(newSalesforceJWTConnector), + providers.Salesloft: wrapper(newSalesloftConnector), + providers.Seismic: wrapper(newSeismicConnector), + providers.Sellsy: wrapper(newSellsyConnector), + providers.ServiceNow: wrapper(newServiceNowConnector), + providers.Shopify: wrapper(newShopifyConnector), + providers.Slack: wrapper(newSlackConnector), + providers.Smartlead: wrapper(newSmartleadConnector), + providers.SnapchatAds: wrapper(newSnapchatAdsConnector), + providers.Snowflake: wrapper(newSnowflakeConnector), + providers.SolarWindsServiceDesk: wrapper(newSolarWindsConnector), + providers.Stripe: wrapper(newStripeConnector), + providers.SuperSend: wrapper(newSuperSendConnector), + providers.Talkdesk: wrapper(newTalkdeskConnector), + providers.Teamleader: wrapper(newTeamleaderConnector), + providers.Teamwork: wrapper(newTeamworkConnector), + providers.Webex: wrapper(newWebexConnector), + providers.Xero: wrapper(newXeroConnector), + providers.ZendeskChat: wrapper(newZendeskChatConnector), + providers.ZendeskSupport: wrapper(newZendeskSupportConnector), + providers.Zoho: wrapper(newZohoConnector), + providers.Zoom: wrapper(newZoomConnector), } type outputConstructorFunc func(p common.ConnectorParams) (connectors.Connector, error) @@ -432,10 +432,10 @@ func newMicrosoftConnector( return microsoft.NewConnector(params) } -func newMicrosoftClientCredentialsConnector( +func newMicrosoftAdminConsentConnector( params common.ConnectorParams, ) (*microsoft.Connector, error) { - return microsoft.NewConnectorForProvider(providers.MicrosoftClientCredentials, params) + return microsoft.NewConnectorForProvider(providers.MicrosoftAdminConsent, params) } func newInstantlyConnector( diff --git a/providers/google/records_test.go b/providers/google/records_test.go index c98e7bd72..33ed86fe5 100644 --- a/providers/google/records_test.go +++ b/providers/google/records_test.go @@ -114,7 +114,7 @@ func TestMailGetRecordsByIds(t *testing.T) { //nolint:funlen ids: []string{idOK1, idMissing}, server: func() *mockserver.Switch { return &mockserver.Switch{ - Setup: mockserver.ContentJSON(), + Setup: mockserver.ContentJSON(), Default: mockserver.Response(http.StatusNotFound, errorNotFound), } }, diff --git a/providers/microsoftClientCredentials.go b/providers/microsoftAdminConsent.go similarity index 50% rename from providers/microsoftClientCredentials.go rename to providers/microsoftAdminConsent.go index 912caa625..9c06de985 100644 --- a/providers/microsoftClientCredentials.go +++ b/providers/microsoftAdminConsent.go @@ -2,21 +2,24 @@ package providers import "net/http" -// MicrosoftClientCredentials is a twin of the Microsoft provider that -// authenticates using the OAuth2 client credentials grant instead of the -// per-user Authorization Code grant. It targets the same Microsoft Graph +// MicrosoftAdminConsent is a twin of the Microsoft provider that authenticates +// using OAuth2 client credentials after an Azure AD admin grants tenant-wide +// consent for application permissions. It targets the same Microsoft Graph // APIs, so the connector implementation in providers/microsoft is reused // under a different provider name. // -// Use cases include admin-consented bulk access (accessing many users' -// mailboxes/calendars without individual OAuth flows), background services, -// and any scenario where the app acts as itself rather than on behalf of a -// signed-in user. -const MicrosoftClientCredentials Provider = "microsoftClientCredentials" +// The setup phase uses the /adminconsent endpoint (redirect-based, like auth +// code) to obtain tenant admin approval. The runtime phase uses the +// client_credentials grant with the ProviderApp's credentials to get +// app-only tokens that can access any user's data in the consented tenant. +// +// See https://learn.microsoft.com/en-us/entra/identity-platform/v2-admin-consent +// See https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-client-creds-grant-flow +const MicrosoftAdminConsent Provider = "microsoftAdminConsent" func init() { - SetInfo(MicrosoftClientCredentials, ProviderInfo{ - DisplayName: "Microsoft", + SetInfo(MicrosoftAdminConsent, ProviderInfo{ + DisplayName: "Microsoft (Admin consent)", AuthType: Oauth2, BaseURL: "https://graph.microsoft.com", AuthHealthCheck: &AuthHealthCheck{ @@ -25,7 +28,14 @@ func init() { Url: "https://graph.microsoft.com/v1.0/organization", }, Oauth2Opts: &Oauth2Opts{ - GrantType: ClientCredentials, + // GrantType is AuthorizationCode so the UI library routes to the + // popup OAuth flow (OauthFlow2) and /v1/oauth-connect generates + // the auth URL. The AuthURL points to Microsoft's admin consent + // endpoint instead of the standard /authorize endpoint. At runtime, + // the connection uses client_credentials for token acquisition + // (AUTH_SCHEME_OAUTH2_CLIENT is set explicitly in the callback). + GrantType: AuthorizationCode, + AuthURL: "https://login.microsoftonline.com/{{.workspace}}/v2.0/adminconsent", TokenURL: "https://login.microsoftonline.com/{{.workspace}}/oauth2/v2.0/token", ExplicitScopesRequired: true, ExplicitWorkspaceRequired: true, @@ -47,8 +57,8 @@ func init() { { Name: "workspace", DisplayName: "Tenant ID", - Prompt: "The Azure AD tenant GUID (e.g. `951a1899-8810-4356-ax10-3a5f8fg99a65`)", - DocsURL: "https://docs.withampersand.com/customer-guides/microsoft-client-credentials", + Prompt: "The Azure AD tenant GUID (e.g. `ab43fdf9-0387-4660-b0dc-00deacc7c3af`). Find it in Azure portal > Microsoft Entra ID > Overview.", + DocsURL: "https://docs.withampersand.com/customer-guides/microsoft-admin-consent", }, }, },