mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 09:36:44 +00:00
fix(Azure OpenAI Chat Model Node): Simplify Azure Entra ID Authentication and Auto-Refresh token (#15335)
This commit is contained in:
@@ -29,6 +29,7 @@ export class CredentialsFlow {
|
||||
const headers: Headers = { ...DEFAULT_HEADERS };
|
||||
const body: CredentialsFlowBody = {
|
||||
grant_type: 'client_credentials',
|
||||
...(options.additionalBodyProperties ?? {}),
|
||||
};
|
||||
|
||||
if (options.scopes !== undefined) {
|
||||
|
||||
@@ -52,31 +52,14 @@ export class AzureEntraCognitiveServicesOAuth2Api implements ICredentialType {
|
||||
{
|
||||
displayName: 'Authorization URL',
|
||||
name: 'authUrl',
|
||||
type: 'string',
|
||||
default: 'https://login.microsoftonline.com/$TENANT_ID/oauth2/authorize',
|
||||
type: 'hidden',
|
||||
default: '=https://login.microsoftonline.com/{{$self["tenantId"]}}/oauth2/authorize',
|
||||
},
|
||||
{
|
||||
displayName: 'Access Token URL',
|
||||
name: 'accessTokenUrl',
|
||||
type: 'string',
|
||||
default: 'https://login.microsoftonline.com/$TENANT_ID/oauth2/token',
|
||||
},
|
||||
{
|
||||
displayName: 'Client ID',
|
||||
name: 'clientId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'Client ID obtained from the Azure AD App Registration',
|
||||
},
|
||||
{
|
||||
displayName: 'Client Secret',
|
||||
name: 'clientSecret',
|
||||
type: 'string',
|
||||
required: true,
|
||||
typeOptions: { password: true },
|
||||
default: '',
|
||||
description: 'Client Secret obtained from the Azure AD App Registration',
|
||||
type: 'hidden',
|
||||
default: '=https://login.microsoftonline.com/{{$self["tenantId"]}}/oauth2/token',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Body Properties',
|
||||
|
||||
@@ -1,9 +1,28 @@
|
||||
import { ClientOAuth2 } from '@n8n/client-oauth2';
|
||||
import type { INode } from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { N8nOAuth2TokenCredential } from '../credentials/N8nOAuth2TokenCredential';
|
||||
import type { AzureEntraCognitiveServicesOAuth2ApiCredential } from '../types';
|
||||
|
||||
// Mock ClientOAuth2
|
||||
jest.mock('@n8n/client-oauth2', () => {
|
||||
return {
|
||||
ClientOAuth2: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
credentials: {
|
||||
getToken: jest.fn().mockResolvedValue({
|
||||
data: {
|
||||
access_token: 'fresh-test-token',
|
||||
expires_on: 1234567890,
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const mockNode: INode = {
|
||||
id: '1',
|
||||
name: 'Mock node',
|
||||
@@ -21,11 +40,12 @@ describe('N8nOAuth2TokenCredential', () => {
|
||||
// Create a mock credential with all required properties
|
||||
mockCredential = {
|
||||
authQueryParameters: '',
|
||||
authentication: 'body', // Set valid authentication type
|
||||
authentication: 'body',
|
||||
authUrl: '',
|
||||
accessTokenUrl: '', // Added missing property
|
||||
grantType: 'clientCredentials', // Corrected grant type value
|
||||
accessTokenUrl: '',
|
||||
grantType: 'clientCredentials',
|
||||
clientId: '',
|
||||
clientSecret: 'secret',
|
||||
customScopes: false,
|
||||
apiVersion: '2023-05-15',
|
||||
endpoint: 'https://test.openai.azure.com',
|
||||
@@ -53,9 +73,15 @@ describe('N8nOAuth2TokenCredential', () => {
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual({
|
||||
token: 'test-token',
|
||||
token: 'fresh-test-token',
|
||||
expiresOnTimestamp: 1234567890,
|
||||
});
|
||||
expect(ClientOAuth2).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
clientId: mockCredential.clientId,
|
||||
clientSecret: mockCredential.clientSecret,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw NodeOperationError when credentials do not contain token', async () => {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import type { TokenCredential, AccessToken } from '@azure/identity';
|
||||
import type { ClientOAuth2TokenData } from '@n8n/client-oauth2';
|
||||
import { ClientOAuth2 } from '@n8n/client-oauth2';
|
||||
import type { INode } from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
@@ -20,10 +22,25 @@ export class N8nOAuth2TokenCredential implements TokenCredential {
|
||||
if (!this.credential?.oauthTokenData?.access_token) {
|
||||
throw new NodeOperationError(this.node, 'Failed to retrieve access token');
|
||||
}
|
||||
const oAuthClient = new ClientOAuth2({
|
||||
clientId: this.credential.clientId,
|
||||
clientSecret: this.credential.clientSecret,
|
||||
accessTokenUri: this.credential.accessTokenUrl,
|
||||
scopes: this.credential.scope?.split(' '),
|
||||
authentication: this.credential.authentication,
|
||||
authorizationUri: this.credential.authUrl,
|
||||
additionalBodyProperties: {
|
||||
resource: 'https://cognitiveservices.azure.com/',
|
||||
},
|
||||
});
|
||||
|
||||
const token = await oAuthClient.credentials.getToken();
|
||||
const data = token.data as ClientOAuth2TokenData & {
|
||||
expires_on: number;
|
||||
};
|
||||
return {
|
||||
token: this.credential.oauthTokenData.access_token,
|
||||
expiresOnTimestamp: this.credential.oauthTokenData.expires_on,
|
||||
token: data.access_token,
|
||||
expiresOnTimestamp: data.expires_on,
|
||||
};
|
||||
} catch (error) {
|
||||
// Re-throw with better error message
|
||||
|
||||
Reference in New Issue
Block a user