Problem with authenticate method for custom node

Describe the problem/error/question

I am working on a custom node for Also Marketplace. Here is a link for Swagger Documentation. Downloadable Postman collection is more up to date.

Basically to retrieve the access token you have to send a POST request with username & password and then use that token in Headers for the operations.

What is the error message (if any)?

500 - "Sender No session token was provided with the request! Please call Authenticate and pass SessionToken.

If I print the sessionToken from credentials, it seems to be generated and also works if used in Postman for any request. This error appears when Authenticate: ‘token’ is not sent in header for the request.

So my guess is authenticate method doesn’t send the sessionToken correctly for my operations. It is a valid token tho.

Please share your workflow

I cannot share a repo, but this is my credentials file:

import {
	IAuthenticateGeneric,
	ICredentialDataDecryptedObject,
	ICredentialTestRequest,
	ICredentialType,
	IHttpRequestHelper,
	INodeProperties,
} from 'n8n-workflow';

export class AlsoMarketPlaceApi implements ICredentialType {
	name = 'alsoMarketPlaceApi';
	displayName = 'Also Marketplace API';
	properties: INodeProperties[] = [
		{
			displayName: 'Username',
			name: 'username',
			type: 'string',
			default: '',
			required: true,
		},
		{
			displayName: 'Password',
			name: 'password',
			type: 'string',
			typeOptions: {
				password: true,
			},
			default: '',
			required: true,
		},
		{
			displayName: 'Base URL',
			name: 'baseURL',
			type: 'string',
			default: '',
			required: true,
		},
		{
			displayName: 'Session Token',
			name: 'sessionToken',
			type: 'hidden',
			typeOptions: {
				expirable: true,
			},
			default: '',
		},
	];

	async preAuthentication(this: IHttpRequestHelper, credentials: ICredentialDataDecryptedObject) {
		// make reques to get access token


		const url = `${credentials.baseURL}/GetSessionToken`;
		const  access_token = await this.helpers.httpRequest({
			method: 'POST',
			url: url,
			headers: {
			},
			body: {
				username: credentials.username,
				password: credentials.password
			},
		});

		console.log('access_token: ')
		console.log(access_token)
		return { sessionToken: access_token };
	}

	authenticate: IAuthenticateGeneric = {
		type: 'generic',
		properties: {
			headers: {
				Authenticate: '={{$credentials.sessionToken}}'
			},
		},
	};

	test: ICredentialTestRequest = {
		request: {
			method: 'POST',
			baseURL: '={{$credentials.baseURL}}',
			url: '/GetMarketplaces',
		},
	};

}

This is the node.ts file with 1 operation:

import {
	IExecuteFunctions,
} from 'n8n-core';

import {
	IHttpRequestOptions,
	INodeExecutionData,
	INodeType,
	INodeTypeDescription,
} from 'n8n-workflow';


export class Alsomarketplace implements INodeType {
	description: INodeTypeDescription = {

		displayName: 'Also Marketplace',
name: 'alsomarketplace',
// eslint-disable-next-line n8n-nodes-base/node-class-description-icon-not-svg
icon: 'file:also-marketplace.png',
group: ['transform'],
version: 1,
subtitle: '={{ $parameter["operation"] + ": " + $parameter["resource"] }}',
description: 'Consume Also Marketplace API',
defaults: {
	name: 'Also Marketplace',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
	{
		name: 'alsoMarketPlaceApi',
		required: true,
	},
],

		properties: [

			{
				displayName: 'Resource',
				name: 'resource',
				type: 'options',
				options: [
					{
						name: 'Marketplace',
						value: 'marketplace',
					},
				],
				default: 'marketplace',
				noDataExpression: true,
				required: true
			},

			{
				displayName: 'Operation',
				name: 'operation',
				type: 'options',
				displayOptions: {
					show: {
						resource: [
							'marketplace',
						],
					},
				},
				options: [
					{
						name: 'Get Many',
						value: 'getMany',
						description: 'Get many marketplaces',
						action: 'Gets company owned marketplaces',
					},
				],
				default: 'getMany',
				noDataExpression: true,
			}

		],
	};

	async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {


const items = this.getInputData();
let responseData;
const returnData = [];
const resource = this.getNodeParameter('resource', 0) as string;
const operation = this.getNodeParameter('operation', 0) as string;

const credentials = await this.getCredentials('alsoMarketPlaceApi');


for (let i = 0; i < items.length; i++) {
	if (resource === 'marketplace') {
		if (operation === 'getMany') {


			const options: IHttpRequestOptions = {
				headers: {
					'Accept': 'application/json'
				},
				method: 'POST',
				body: {},
				url: `${credentials.baseURL}/GetMarketplaces`,
				json: true,
			};

			console.log(credentials)
			responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'alsoMarketPlaceApi', options);
			returnData.push(responseData);
		}
	}
}

return [this.helpers.returnJsonArray(returnData)];

	}
}


Share the output returned by the last node

Here is the form of the request in Postman that is working for get many marketplaces:

Can someone help me with this as project is stopped due to this error? I also noticed the sessionToken is not re-generated between requests. How exactly is that expirable: true working?

If I get the sessionToken from credentials and attach it to header in node.ts file, the request works but this is not ideal as we want to reuse the connection for http request module.

Thank you in advance, let me know if there is any info I should provide!

Information on your n8n setup

  • n8n version: 0.236.2
  • Running n8n via (Docker, npm, n8n cloud, desktop app): npm
  • Operating system: MAC

Tested with a hardcoded header inside authenticate and same issue. It doesn’t seem to work as it’s supposed to in my code

Hi @alexnemes - sorry you’re having trouble with this! Maybe our resident node-builder @marcus might have some insight on how to help? :bowing_man:

Even if I try the connection like this with a valid session token in Authenticate header that works in Postman, still the authenticate method doesn’t perform:

import {
	IAuthenticateGeneric,
	ICredentialType,
	INodeProperties,
} from 'n8n-workflow';

export class AlsoMarketPlaceApi implements ICredentialType {
	name = 'alsoMarketPlaceApi';
	displayName = 'Also Marketplace API';
	properties: INodeProperties[] = [
		{
			displayName: 'Base URL',
			name: 'baseURL',
			type: 'string',
			default: '',
			required: true,
		},
	];

	authenticate: IAuthenticateGeneric = {
		type: 'generic',
		properties: {
			headers: {
				'Authenticate': 'xyz'
			},
		},
	};

}

I am using n8n’s latest version 0.236.2

Hi @alexnemes,
I tried your simplified credential and it’s definetly setting the Authenticate header.

According to the swagger doc the Authenticate Header needs to have a CCPSessionId prefix.

So maybe your credential’s authenticate method needs to look like this

authenticate: IAuthenticateGeneric = {
		type: 'generic',
		properties: {
			headers: {
				Authenticate: '=CCPSessionId {{$credentials.sessionToken}}'
			},
		},
	};
1 Like

Hi @marcus thanks for looking into it.

I tried and still same error. I know they require CCPSessionId in Swagger doc but in their Postman collection it works even without it (they use there authentication type API Key and key Authenticate). If I print the sessionToken, it is valid. If I try it in Postman, it works.:


Anyway, added CCPSessionId in authenticate method and still same result. Can I somehow print the whole request in n8n to see exactly the header attached?

image

Thank you!

Apparently it works if I use requestWithAuthentication instead of httpRequestWithAuthentication for my operation.

Also, the test request says invalid credentials but same operation marketplace: getMany works in node.

Thanks a lot for your help, it is solved now :smiley:

1 Like

@marcus I noticed a strange behavior, for this get many operation, it says result has 1 item although the array definitely has more:

I also get this error if I try the app’s connection with http request node. It also seems to not be working even if I select no auth and then pass a valid token in header with the CCPSessionId prefix:

image

Hi @alexnemes,
that looks very strange indeed. What does the output show in the Table view.

@marcus looks like this:

Are you perhaps returning an array of arrays in your node code?

2 Likes

Hi @marcus yes, indeed. This is how I fixed it:

Is there a better approach to handle this? For example in cases when the operation will throw error, this function would prevent output from getting the actual api error response object

1 Like

Hi @alexnemes,
whenever your requestWithAuthentication call throws an error it should automatically be visible as an error in your execution view. Is that not happening with your custom node implementation?

1 Like

If I use the responseDataArr above (so I expect the successful response is an array and I want to push each element in returnData) and there is an error for the operation, this will be the output instead of the actual error from the request (in this case I intentionally formed a bad URL):

The correct error message would be printed if I use responseData as any, and not the responseDataArr expecting an array:

Hope this makes sense. I want to know if I can somehow consider the array response when there is a success response or else, push the json object to returnData, which is the error for the request.

Thank you