Skip to content

Commit

Permalink
feat: enhance OAuth2 node with additional fields and implicit flow su…
Browse files Browse the repository at this point in the history
…pport

- Update .eslintrc.yml to exclude console logs in test files.
- Modify package.json to bump version to 6.0.0 and update dependencies.
- Add new fields (access_type, response_type, prompt) to locales, HTML, and JS files.
- Implement implicit flow in OAuth2 node, with corresponding HTML form adjustments.
- Improve input handling and proxy configuration in OAuth2Node class.
- Add tests for new fields and flows.
  • Loading branch information
caputomarcos committed May 21, 2024
1 parent 1efdac8 commit ea632b2
Show file tree
Hide file tree
Showing 7 changed files with 1,029 additions and 172 deletions.
7 changes: 5 additions & 2 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ env:
node: true
jasmine: true
overrides:
- files:
- "*.js"
- files: ["test/**/*.js"]
rules:
no-console: "off"
# - files:
# - "*.js"
rules:
# Exemplo de regras personalizadas para arquivos JavaScript
semi: [2, 'always'] # Verificar se há ponto e vírgula ausente
Expand Down
676 changes: 676 additions & 0 deletions diff

Large diffs are not rendered by default.

13 changes: 9 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "node-red-contrib-oauth2",
"version": "5.2.7",
"version": "6.0.0",
"description": "The node-red-contrib-oauth2 is a Node-RED node that provides an OAuth2 authentication flow. This node uses the OAuth2 protocol to obtain an access token, which can be used to make authenticated API requests.",
"author": "Marcos Caputo <caputo.marcos@gmail.com>",
"contributors": [
Expand Down Expand Up @@ -35,7 +35,7 @@
},
"dependencies": {
"axios": ">=1.3.3",
"json-schema": ">=0.4.0"
"mocha": "^10.4.0"
},
"devDependencies": {
"@babel/eslint-parser": "^7.21.8",
Expand All @@ -49,7 +49,11 @@
"eslint-plugin-promise": "^6.1.1",
"jsdoc": "^4.0.3",
"json-schema": ">=0.4.0",
"prettier": "^2.8.8"
"nock": "^13.5.4",
"node-red": "^3.1.9",
"node-red-node-test-helper": "^0.3.4",
"prettier": "^2.8.8",
"should": "^13.2.3"
},
"eslintConfig": {
"extends": "./.eslintrc.yml"
Expand All @@ -58,6 +62,7 @@
"fix": "npx eslint ./src/. && npx eslint ./src/. --fix",
"lint": "prettier --plugin-search-dir . --check ./src/. && npx eslint ./src/.",
"format": "prettier --plugin-search-dir . --write ./src/.",
"doc": "jsdoc -c jsdoc.json"
"doc": "jsdoc -c jsdoc.json",
"test": "mocha \"test/**/*_spec.js\""
}
}
7 changes: 7 additions & 0 deletions src/locales/en-US/oauth2.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
"password": "Password",
"client_id": "Client ID",
"client_secret": "Client Secret",
"access_type": "Access Type",
"response_type": "Response Type",
"prompt": "Prompt",
"scope": "Scope",
"resource": "Resource",
"state": "State",
Expand All @@ -35,6 +38,9 @@
"password": "admin",
"client_id": "012493af6282be51660dbc8e21a8462e",
"client_secret": "5621bd4b5a8b09ed31817efb8d54fda2c72bfc1c6968cd4563d83f7cc26f68f6",
"access_type": "offline",
"response_type": "code",
"prompt": "consent",
"scope": "scope",
"resource": "resource",
"state": "state",
Expand All @@ -45,6 +51,7 @@
"client_credentials": "Client Credentials",
"password_credentials": "Password",
"authorization_code": "Authorization Code",
"implicit_flow": "Implicit Flow",
"set_by_credentials": "- Set by msg.oauth2Request -"
}
}
Expand Down
150 changes: 105 additions & 45 deletions src/oauth2.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<option value="client_credentials" data-i18n="oauth2.opts.client_credentials"></option>
<option value="password" data-i18n="oauth2.opts.password_credentials"></option>
<option value="authorization_code" data-i18n="oauth2.opts.authorization_code"></option>
<option value="implicit_flow" data-i18n="oauth2.opts.implicit_flow"></option>
<option value="set_by_credentials" data-i18n="oauth2.opts.set_by_credentials"></option>
</select>
</div>
Expand Down Expand Up @@ -52,6 +53,22 @@
<label for="node-input-client_secret"><i class="fa fa-lock fa-fw"></i> <span data-i18n="oauth2.label.client_secret"></span></label>
<input type="password" id="node-input-client_secret" data-i18n="[placeholder]oauth2.placeholder.client_secret" style="width:70%;" />
</div>

<div class="form-row" id="node-access_type">
<label for="node-input-access_type"><i class="fa fa-link fa-fw"></i> <span data-i18n="oauth2.label.access_type"></span></label>
<input type="text" id="node-input-access_type" data-i18n="[placeholder]oauth2.placeholder.access_type" style="width:70%;" />
</div>
<!-- node-response_type -->
<div class="form-row" id="node-response_type">
<label for="node-input-response_type"><i class="fa fa-link fa-fw"></i> <span data-i18n="oauth2.label.response_type"></span></label>
<input type="text" id="node-input-response_type" data-i18n="[placeholder]oauth2.placeholder.response_type" style="width:70%;" />
</div>
<!-- node-prompt -->
<div class="form-row" id="node-prompt">
<label for="node-input-prompt"><i class="fa fa-link fa-fw"></i> <span data-i18n="oauth2.label.prompt"></span></label>
<input type="text" id="node-input-prompt" data-i18n="[placeholder]oauth2.placeholder.prompt" style="width:70%;" />
</div>

<!-- node-scope -->
<div class="form-row" id="node-scope">
<label for="node-input-scope"><i class="fa fa-code fa-fw"></i> <span data-i18n="oauth2.label.scope"></span></label>
Expand Down Expand Up @@ -147,7 +164,7 @@
icon: 'red/images/typedInput/az.svg'
}
];

RED.nodes.registerType('oauth2', {
category: 'DevSecOps',
color: '#fff',
Expand All @@ -157,12 +174,15 @@
grant_type: { value: 'set_by_credentials' },
access_token_url: { value: '' },
authorization_endpoint: { value: '' },
redirect_uri: { value: '/oauth2/redirect_uri' },
redirect_uri: { value: '' },
open_authentication: { value: '' },
username: { value: '' },
password: { value: '' },
client_id: { value: '' },
client_secret: { value: '' },
response_type: { value: '' },
access_type: { value: '' },
prompt: { value: '' },
scope: { value: '' },
resource: { value: '' },
state: { value: '' },
Expand Down Expand Up @@ -202,7 +222,9 @@
callback = `${location.protocol}//${location.hostname}${location.port ? ':' + location.port : ''}${pathname}oauth2/auth/callback`;
}

// TODO - Aqui nasce o MOSTRO, está feio mas funciona!
const redirectUri = `${location.protocol}//${location.hostname}${location.port ? ':' + location.port : ''}${pathname}oauth2/redirect`;
this.redirect_uri = redirectUri;

if (this.container === undefined) {
$('#node-input-container').val('payload');
Expand All @@ -214,18 +236,46 @@
});

const elementMapping = {
'set_by_credentials': ['#node-rejectUnauthorized', '#node-client_credentials_in_body'],
'client_credentials': ['#node-access_token_url', '#node-client_id', '#node-client_secret', '#node-scope', '#node-resource', '#node-state', '#node-rejectUnauthorized', '#node-client_credentials_in_body'],
'password': ['#node-password_credentials', '#node-access_token_url', '#node-client_id', '#node-client_secret', '#node-scope', '#node-resource', '#node-state', '#node-rejectUnauthorized', '#node-client_credentials_in_body'],
'authorization_code': ['#node-open_authentication', '#node-redirect_uri', '#node-access_token_url', '#node-authorization_endpoint', '#node-client_id', '#node-client_secret', '#node-scope', '#node-resource', '#node-state', '#node-rejectUnauthorized', '#node-client_credentials_in_body']
set_by_credentials: ['#node-rejectUnauthorized', '#node-client_credentials_in_body'],
client_credentials: ['#node-access_token_url', '#node-client_id', '#node-client_secret', '#node-scope', '#node-resource', '#node-state', '#node-rejectUnauthorized', '#node-client_credentials_in_body'],
password: ['#node-password_credentials', '#node-access_token_url', '#node-client_id', '#node-client_secret', '#node-scope', '#node-resource', '#node-state', '#node-rejectUnauthorized', '#node-client_credentials_in_body'],
authorization_code: [
'#node-open_authentication',
'#node-redirect_uri',
'#node-access_token_url',
'#node-authorization_endpoint',
'#node-client_id',
'#node-client_secret',
'#node-scope',
'#node-resource',
'#node-state',
'#node-rejectUnauthorized',
'#node-client_credentials_in_body'
],
implicit_flow: [
'#node-open_authentication',
'#node-redirect_uri',
'#node-access_token_url',
'#node-authorization_endpoint',
'#node-client_id',
'#node-client_secret',
'#node-access_type',
'#node-response_type',
'#node-prompt',
'#node-scope',
'#node-resource',
'#node-state',
'#node-rejectUnauthorized',
'#node-client_credentials_in_body'
]
};

function updateVisibility() {
const grantType = $('#node-input-grant_type').val();
for (const key in elementMapping) {
elementMapping[key].forEach(selector => $(selector).hide());
elementMapping[key].forEach((selector) => $(selector).hide());
}
elementMapping[grantType].forEach(selector => $(selector).show());
elementMapping[grantType].forEach((selector) => $(selector).show());
RED.tray.resize();
}

Expand All @@ -246,17 +296,24 @@
$('#authorizeButton').mousedown(function () {
const authorizationEndpoint = $('#node-input-authorization_endpoint').val();
const clientId = $('#node-input-client_id').val();
const clientSecret = $('#node-input-client_secret').val();
const proxy = $('#node-input-proxy').val();
let scope = $('#node-input-scope').val().replace(/\n/g, '%20');
let resource = $('#node-input-resource').val().replace(/\n/g, '%20');
let state = $('#node-input-state').val().replace(/\n/g, '%20');
let url;
const scope = $('#node-input-scope').val().replace(/\n/g, '%20');
const resource = $('#node-input-resource').val().replace(/\n/g, '%20');
const state = $('#node-input-state').val().replace(/\n/g, '%20');

if (authorizationEndpoint) {
url = `oauth2/auth?id=${encodeURIComponent(id)}&clientId=${encodeURIComponent(clientId)}&clientSecret=${encodeURIComponent(clientSecret)}&scope=${encodeURIComponent(scope)}&state=${encodeURIComponent(state)}&resource=${encodeURIComponent(resource)}&callback=${encodeURIComponent(callback)}&authorizationEndpoint=${encodeURIComponent(authorizationEndpoint)}&redirectUri=${encodeURIComponent(redirectUri)}&proxy=${encodeURIComponent(proxy)}`;
} else {
url = `oauth2/auth?id=${encodeURIComponent(id)}&clientId=${encodeURIComponent(clientId)}&clientSecret=${encodeURIComponent(clientSecret)}&scope=${encodeURIComponent(scope)}&state=${encodeURIComponent(state)}&resource=${encodeURIComponent(resource)}&callback=${encodeURIComponent(callback)}&proxy=${encodeURIComponent(proxy)}`;
const accessType = $('#node-input-access_type').val();
const responseType = $('#node-input-response_type').val();
const prompt = $('#node-input-prompt').val();

let url;
if (accessType) {
url = `${authorizationEndpoint}?id=${encodeURIComponent(id)}&redirect_uri=${encodeURIComponent(redirectUri)}&client_id=${encodeURIComponent(clientId)}&response_type=${responseType}&scope=${encodeURIComponent(
scope
)}&access_type=${encodeURIComponent(accessType)}&prompt=${encodeURIComponent(prompt)}&state=${encodeURIComponent(id)}:node_id`;
} else if (authorizationEndpoint) {
url = `${authorizationEndpoint}?id=${encodeURIComponent(id)}&redirect_uri=${encodeURIComponent(redirectUri)}&client_id=${encodeURIComponent(clientId)}&response_type=code&scope=${encodeURIComponent(scope)}&resource=${encodeURIComponent(
resource
)}&state=${encodeURIComponent(id)}:node_id`;
}
$(this).attr('href', url);
window.configNodeIntervalId = window.setTimeout(pollCredentials, 5000);
Expand Down Expand Up @@ -292,33 +349,35 @@
.css('min-width', '450px')
.editableList({
addItem: function (container, i, header) {
const row = $('<div/>')
.css({ overflow: 'hidden', whiteSpace: 'nowrap' })
.appendTo(container);
const row = $('<div/>').css({ overflow: 'hidden', whiteSpace: 'nowrap' }).appendTo(container);

const propertyName = $('<input/>', {
class: 'node-input-header-name',
type: 'text',
style: 'width: 50%'
}).appendTo(row).typedInput({ types: headerTypes });
})
.appendTo(row)
.typedInput({ types: headerTypes });

const propertyValue = $('<input/>', {
class: 'node-input-header-value',
type: 'text',
style: 'margin-left: 10px; width: 45%;'
}).appendTo(row).typedInput({
types: header.h === 'content-type' ? contentTypes : [{ value: 'other', label: RED._('node-red:httpin.label.other'), hasValue: true, icon: 'red/images/typedInput/az.svg' }]
});
})
.appendTo(row)
.typedInput({
types: header.h === 'content-type' ? contentTypes : [{ value: 'other', label: RED._('node-red:httpin.label.other'), hasValue: true, icon: 'red/images/typedInput/az.svg' }]
});

const matchedType = headerTypes.filter(ht => ht.value === header.h);
const matchedType = headerTypes.filter((ht) => ht.value === header.h);
if (matchedType.length === 0) {
propertyName.typedInput('type', 'other');
propertyName.typedInput('value', header.h);
propertyValue.typedInput('value', header.v);
} else {
propertyName.typedInput('type', header.h);
if (header.h === 'content-type') {
const matchedContentType = contentTypes.filter(ct => ct.value === header.v);
const matchedContentType = contentTypes.filter((ct) => ct.value === header.v);
if (matchedContentType.length === 0) {
propertyValue.typedInput('type', 'other');
propertyValue.typedInput('value', header.v);
Expand Down Expand Up @@ -359,24 +418,26 @@
}
const headers = $('#node-input-headers-container').editableList('items');
this.headers = {};
headers.each(function () {
const header = $(this);
const keyType = header.find('.node-input-header-name').typedInput('type');
const keyValue = header.find('.node-input-header-name').typedInput('value');
const valueType = header.find('.node-input-header-value').typedInput('type');
const valueValue = header.find('.node-input-header-value').typedInput('value');
let key = keyType;
let value = valueType;
if (keyType === 'other') {
key = keyValue;
}
if (valueType === 'other') {
value = valueValue;
}
if (key !== '') {
this.headers[key] = value;
}
}.bind(this));
headers.each(
function () {
const header = $(this);
const keyType = header.find('.node-input-header-name').typedInput('type');
const keyValue = header.find('.node-input-header-name').typedInput('value');
const valueType = header.find('.node-input-header-value').typedInput('type');
const valueValue = header.find('.node-input-header-value').typedInput('value');
let key = keyType;
let value = valueType;
if (keyType === 'other') {
key = keyValue;
}
if (valueType === 'other') {
value = valueValue;
}
if (key !== '') {
this.headers[key] = value;
}
}.bind(this)
);
},
oneditresize: function (size) {
const dlg = $('#dialog-form');
Expand All @@ -394,4 +455,3 @@
});
})();
</script>

Loading

0 comments on commit ea632b2

Please sign in to comment.