oAuth2 server create issue

Hi
I am trying to implement an oAuth2 server within node-red. One of the requirements is a model to be used which is declared as follows:

const OAuth2Server=global.get('OAuth2Server')
Request = OAuth2Server.Request,
Response = OAuth2Server.Response

const oauth = new OAuth2Server({
  model: global.get('model'),
  accessTokenLifetime: 60 * 60,
  allowBearerTokensInQueryString: true
});

Both OAuth2Server and model are defined in settings.js as:

functionGlobalContext: {
        os:require('os'),
        OAuth2Server:require('oauth2-server'),
        model:require('./model.js'),
    }    // enables global context
};

This works fine, but it limits me to have an external .js file to maintain.
I would like to have model.js as a function or config element or whatever might be suggested by the community, to enable me to edit the model from within node-red. I do not want to ship an additional .js file during deployment. I would also like to be able to create an oAuth2 server node for community distribution.

I would really appreciate some guidance. Below an excerpt from the model.js file:

/**
 * Configuration.
 */

var config = {
	clients: [{
		id: 'application',	// TODO: Needed by refresh_token grant, because there is a bug at line 103 in https://github.com/oauthjs/node-oauth2-server/blob/v3.0.1/lib/grant-types/refresh-token-grant-type.js (used client.id instead of client.clientId)
		clientId: 'application',
		clientSecret: 'secret',
		grants: [
			'password',
			'refresh_token'
		],
		redirectUris: []
	}],
	confidentialClients: [{
		clientId: 'confidentialApplication',
		clientSecret: 'topSecret',
		grants: [
			'password',
			'client_credentials'
		],
		redirectUris: []
	}],
	tokens: [],
	users: [{
		username: 'pedroetb',
		password: 'password'
	}]
};

/*
 * Methods used by all grant types.
 */

var getAccessToken = function(token) {

	var tokens = config.tokens.filter(function(savedToken) {

		return savedToken.accessToken === token;
	});

	return tokens[0];
};

var getClient = function(clientId, clientSecret) {

	var clients = config.clients.filter(function(client) {

		return client.clientId === clientId && client.clientSecret === clientSecret;
	});

	var confidentialClients = config.confidentialClients.filter(function(client) {

		return client.clientId === clientId && client.clientSecret === clientSecret;
	});

	return clients[0] || confidentialClients[0];
};
module.exports = {
	getAccessToken: getAccessToken,
	getClient: getClient,
	saveToken: saveToken,
	getUser: getUser,
	getUserFromClient: getUserFromClient,
	getRefreshToken: getRefreshToken,
	revokeToken: revokeToken
};

Regards
Morne

There are already oAuth2 contrib nodes available in the flow library.

Do these not work for you?

Perhaps you could contribute to one of them to get it up to a good level (if need be of course)?


you can simply copy the code from model.js to a function node & store it in global context. (change the module.exports = { to something like var myModel = { then store myModel in global context). you would need to trigger the function node once on start up (e.g. an inject node set to fire on start up)

Hi @Steve-Mcl, thanks for the reply.
If I am wrong, please forgive, but it looks like the oAuth2 contrib nodes are for clients and not server side.

I have previously tried something similar, but not as you suggested with the module.exports part, which kept on failing. It is still failing at the same point, but it looks like I am going in the right direction.

I will update this issue with results.

Regards
Morne

My apologies, I think you are right.

Hopefully the other info is of use.

If you do go on to create a contrib node, this should get you started.

Hi @Steve-Mcl, no worries. Yeah, I am sure that your info will help. If I can get this to work, I will definitely contribute by creating a server node.

Regards
Morne

Hi
Ok, here is a status update and observations of the solution that is not working, but not due to the advice given by the community:

  1. I have a model.js acquired via settings.js require that works as expected
const oauth = new OAuth2Server({
  model: global.get('model'),
  accessTokenLifetime: 60 * 60,
  allowBearerTokensInQueryString: true
});
  1. I also have an exact copy of model.js in a node-red function that I inject via inject node when flow restarts and it is acquired via:
const oauth = new OAuth2Server({
  model: global.get('myModel'),
  accessTokenLifetime: 60 * 60,
  allowBearerTokensInQueryString: true
});
  1. When I test with model acquired via point 1, I get the results as expected.
  2. When I test with the model acquired via point 2, the test fails with:
Server error: `grants` must be an array

I tested with multiple tests and grants is an array:

{
  clientId: 'confidentialApplication',
  clientSecret: 'topSecret',
  grants: [ 'password', 'client_credentials' ],
  redirectUris: []
}
  1. The oauth2-server nodejs module has a handler that tests if grants is an array with:
if (!(client.grants instanceof Array)) {
        console.log("handler grants: "+client.grants)
        throw new ServerError('Server error: `grants` must be an array');
      }

and the test fails, even though the way I create the client object is exactly the same in point 1 and point 2.
6. If I though change the test to:

if (!(Array.isArray(client.grants))) {
        throw new ServerError('Server error: `grants` must be an array');
      }

the test is successful and I get the results I expect.

I now have the question: What is the difference between the instanceof and Array.isArray test? Is there anything I can do in node-red to ensure that the instanceof test will pass?

Regards
Morne