From 93fa40c39503bf7f413d7306d60ea9c077bd7ff0 Mon Sep 17 00:00:00 2001 From: Stephen Wright Date: Tue, 14 Jul 2026 12:41:02 +0100 Subject: [PATCH] feat(Microsoft Excel SharePoint Node): Add sign-in with person and app credentials (no-changelog) (#34089) --- .../MicrosoftExcelSharePoint.node.ts | 46 ++++++++++++++++- .../MicrosoftExcelSharePoint.node.test.ts | 7 +-- .../test/authentication.test.ts | 51 +++++++++++++++++++ 3 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 packages/nodes-base/nodes/Microsoft/ExcelSharePoint/test/authentication.test.ts diff --git a/packages/nodes-base/nodes/Microsoft/ExcelSharePoint/MicrosoftExcelSharePoint.node.ts b/packages/nodes-base/nodes/Microsoft/ExcelSharePoint/MicrosoftExcelSharePoint.node.ts index 7c1e5e80baf..9305d22a3e1 100644 --- a/packages/nodes-base/nodes/Microsoft/ExcelSharePoint/MicrosoftExcelSharePoint.node.ts +++ b/packages/nodes-base/nodes/Microsoft/ExcelSharePoint/MicrosoftExcelSharePoint.node.ts @@ -22,7 +22,51 @@ export class MicrosoftExcelSharePoint implements INodeType { inputs: [NodeConnectionTypes.Main], outputs: [NodeConnectionTypes.Main], hidden: true, - properties: [], + // Legacy credentials deliberately excluded: the SharePoint one targets + // the old _api host (not Graph); the Excel one has no Sites.* scopes. + credentials: [ + { + name: 'microsoftOAuth2Api', + required: true, + displayOptions: { + show: { + authentication: ['microsoftOAuth2Api'], + }, + }, + }, + { + name: 'microsoftEntraServicePrincipalApi', + required: true, + displayOptions: { + show: { + authentication: ['microsoftEntraServicePrincipalApi'], + }, + }, + }, + ], + properties: [ + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + noDataExpression: true, + options: [ + { + name: 'Microsoft OAuth2 (Graph)', + value: 'microsoftOAuth2Api', + description: + 'Generic Microsoft Graph credential. Enable the scopes this node needs (e.g. Sites.ReadWrite.All) on the credential.', + }, + { + name: 'Microsoft Entra Service Principal (App-Only)', + value: 'microsoftEntraServicePrincipalApi', + description: + 'App-only access via a Microsoft Entra app registration with admin-consented SharePoint application permissions', + }, + ], + default: 'microsoftOAuth2Api', + }, + ], }; // Pass-through until the first action arrives with the router. diff --git a/packages/nodes-base/nodes/Microsoft/ExcelSharePoint/test/MicrosoftExcelSharePoint.node.test.ts b/packages/nodes-base/nodes/Microsoft/ExcelSharePoint/test/MicrosoftExcelSharePoint.node.test.ts index b9fbcebcd08..d5bcc389fa1 100644 --- a/packages/nodes-base/nodes/Microsoft/ExcelSharePoint/test/MicrosoftExcelSharePoint.node.test.ts +++ b/packages/nodes-base/nodes/Microsoft/ExcelSharePoint/test/MicrosoftExcelSharePoint.node.test.ts @@ -23,10 +23,11 @@ describe('MicrosoftExcelSharePoint (hidden shell)', () => { expect(node.description.displayName).not.toBe(oneDriveNode.description.displayName); }); - it('should be a blank shell — no credentials, no properties', () => { + it('should stay a shell — sign-in only, no operations or tool exposure', () => { expect(node.description.version).toBe(1); - expect(node.description.properties).toHaveLength(0); - expect(node.description.credentials).toBeUndefined(); + // Any property beyond the sign-in selector would silently no-op through + // the pass-through execute; the first action ticket updates this bound. + expect(node.description.properties).toHaveLength(1); expect(node.description.usableAsTool).toBeUndefined(); }); diff --git a/packages/nodes-base/nodes/Microsoft/ExcelSharePoint/test/authentication.test.ts b/packages/nodes-base/nodes/Microsoft/ExcelSharePoint/test/authentication.test.ts new file mode 100644 index 00000000000..56bceb6d722 --- /dev/null +++ b/packages/nodes-base/nodes/Microsoft/ExcelSharePoint/test/authentication.test.ts @@ -0,0 +1,51 @@ +import { MicrosoftExcelSharePoint } from '../MicrosoftExcelSharePoint.node'; + +describe('Microsoft Excel (SharePoint) authentication selector', () => { + const { description } = new MicrosoftExcelSharePoint(); + const properties = description.properties; + const credentials = description.credentials ?? []; + const authProperty = properties.find((property) => property.name === 'authentication'); + const optionValues = (authProperty?.options ?? []).map((option) => + 'value' in option ? option.value : undefined, + ); + + it('should expose an authentication options selector as the first property', () => { + expect(authProperty).toBeDefined(); + expect(authProperty?.type).toBe('options'); + expect(authProperty?.noDataExpression).toBe(true); + expect(properties[0]?.name).toBe('authentication'); + }); + + it('should offer exactly the generic Microsoft and Service Principal credentials', () => { + expect(optionValues).toEqual(['microsoftOAuth2Api', 'microsoftEntraServicePrincipalApi']); + }); + + it('should default to the generic Microsoft credential', () => { + expect(authProperty?.default).toBe('microsoftOAuth2Api'); + }); + + it('should gate each credential behind its matching authentication value', () => { + expect(credentials).toHaveLength(2); + for (const credential of credentials) { + expect(credential.required).toBe(true); + expect(credential.displayOptions?.show?.authentication).toEqual([credential.name]); + } + }); + + it('should not offer the legacy SharePoint or Excel (OneDrive) credentials', () => { + const credentialNames = credentials.map((credential) => credential.name); + + for (const legacyCredential of ['microsoftSharePointOAuth2Api', 'microsoftExcelOAuth2Api']) { + expect(optionValues).not.toContain(legacyCredential); + expect(credentialNames).not.toContain(legacyCredential); + } + }); + + it('should keep the default aligned with a gated credential', () => { + const credentialGatedByDefault = credentials.find((credential) => + credential.displayOptions?.show?.authentication?.includes(authProperty?.default as string), + ); + + expect(credentialGatedByDefault?.name).toBe('microsoftOAuth2Api'); + }); +});