UNPKG

node-red-contrib-oauth2

Version:

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.

497 lines (464 loc) 25.9 kB
<script type="text/html" data-template-name="oauth2"> <!-- node-input-name --> <div class="form-row"> <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="oauth2.label.name"></span></label> <input type="text" id="node-input-name" data-i18n="[placeholder]oauth2.placeholder.name" style="width:70%;" /> </div> <!-- node-input-container --> <div class="form-row"> <label for="node-input-container"><i class="fa fa-ellipsis-h"></i> <span data-i18n="oauth2.label.container"></span></label> <input type="text" id="node-input-container" data-i18n="[placeholder]oauth2.placeholder.container" style="width:70%;" /> </div> <!-- node-input-grant_type --> <div class="form-row"> <label for="node-input-grant_type"><i class="fa fa-wrench"></i> <span data-i18n="oauth2.label.grant_type"></span></label> <select type="text" id="node-input-grant_type" style="width:70%;"> <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="refresh_token" data-i18n="oauth2.opts.refresh_token"></option> <option value="set_by_credentials" data-i18n="oauth2.opts.set_by_credentials"></option> </select> </div> <!-- node-access_token_url --> <div class="form-row" id="node-access_token_url"> <label for="node-input-access_token_url"><i class="fa fa-link fa-fw"></i> <span data-i18n="oauth2.label.access_token_url"></span></label> <input type="text" id="node-input-access_token_url" data-i18n="[placeholder]oauth2.placeholder.access_token_url" style="width:70%;" /> </div> <!-- node-authorization_endpoint --> <div class="form-row" id="node-authorization_endpoint"> <label for="node-input-authorization_endpoint"><i class="fa fa-link fa-fw"></i> <span data-i18n="oauth2.label.authorization_endpoint"></span></label> <input type="text" id="node-input-authorization_endpoint" data-i18n="[placeholder]oauth2.placeholder.authorization_endpoint" style="width:70%;" /> </div> <!-- node-password_credentials --> <div class="form-row" id="node-password_credentials"> <!--node-input-username --> <div class="form-row"> <label for="node-input-username"><i class="fa fa-user fa-fw"></i> <span data-i18n="oauth2.label.username"></span></label> <input type="text" id="node-input-username" data-i18n="[placeholder]oauth2.placeholder.username" style="width:70%;" /> </div> <!-- node-input-password --> <div class="form-row"> <label for="node-input-password"><i class="fa fa-lock fa-fw"></i> <span data-i18n="oauth2.label.password"></span></label> <input type="password" id="node-input-password" data-i18n="[placeholder]oauth2.placeholder.password" style="width:70%;" /> </div> </div> <!-- node-client_id --> <div class="form-row" id="node-client_id"> <label for="node-input-client_id"><i class="fa fa-user fa-fw"></i> <span data-i18n="oauth2.label.client_id"></span></label> <input type="text" id="node-input-client_id" data-i18n="[placeholder]oauth2.placeholder.client_id" style="width:70%;" /> </div> <!-- node-client_secret --> <div class="form-row" id="node-client_secret"> <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> <!-- node-refresh_token --> <div class="form-row" id="node-refresh_token"> <label for="node-input-refresh_token"><i class="fa fa-lock fa-fw"></i> <span data-i18n="oauth2.label.refresh_token"></span></label> <input type="password" id="node-input-refresh_token" data-i18n="[placeholder]oauth2.placeholder.refresh_token" 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> <input type="text" id="node-input-scope" data-i18n="[placeholder]oauth2.placeholder.scope" style="width:70%;" /> </div> <!-- node-resource --> <div class="form-row" id="node-resource"> <label for="node-input-resource"><i class="fa fa-code fa-fw"></i> <span data-i18n="oauth2.label.resource"></span></label> <input type="text" id="node-input-resource" data-i18n="[placeholder]oauth2.placeholder.resource" style="width:70%;" /> </div> <!-- node-state --> <div class="form-row" id="node-state"> <label for="node-input-state"><i class="fa fa-code fa-fw"></i> <span data-i18n="oauth2.label.state"></span></label> <input type="text" id="node-input-state" data-i18n="[placeholder]oauth2.placeholder.state" style="width:70%;" /> </div> <!-- node-open_authentication --> <div class="form-row" id="node-open_authentication"> <label for="node-input-open_authentication"><i class="fa fa-sign-in fa-fw"></i> <span data-i18n="oauth2.label.open_authentication"></span></label> <a class="red-ui-button fa fa-key" id="authorizeButton" href="#" target="_blank"></a> <input type="password" id="node-input-open_authentication" data-i18n="[placeholder]oauth2.placeholder.open_authentication" style="width:62%;margin-top: -2px;" disabled /> </div> <div class="form-row"> <input type="checkbox" id="node-input-useProxy" style="display: inline-block; width: auto; vertical-align: top;" /> <label for="node-input-useProxy" style="width: auto;"><span data-i18n="oauth2.label.use-proxy"></span></label> <div id="node-input-useProxy-row" class="hide"> <label style="width: auto; margin-left: 20px; margin-right: 10px;" for="node-input-proxy"><i class="fa fa-globe"></i> <span data-i18n="oauth2.label.proxy-config"></span></label><input type="text" style="width: 270px" id="node-input-proxy" /> </div> </div> <div class="form-row"> <input type="checkbox" id="node-input-senderr" style="display: inline-block; width: auto; vertical-align: top;" /> <label for="node-input-senderr" style="width: auto;" data-i18n="oauth2.label.senderr"></label> </div> <!-- node-debug --> <div class="form-row"> <input type="checkbox" id="node-input-debug" style="display: inline-block; width: auto; vertical-align: top;" /> <label for="node-input-debug" style="width:auto;"><i class="fa fa-sign-in fa-fw"></i> <span data-i18n="oauth2.label.debug"></span></label> <div abria-label="Info for context store" class="form-row form-tips node-help" style="width:100%"> <i class="fa fa-exclamation-circle fa-fw"></i><b>Debug Mode</b> - <span data-i18n="oauth2.tips.debug"></span> </div> </div> <!-- node-force --> <div class="form-row"> <input type="checkbox" id="node-input-force" style="display: inline-block; width: auto; vertical-align: top;" /> <label for="node-input-force" style="width:auto;"><i class="fa fa-sign-in fa-fw"></i> <span data-i18n="oauth2.label.force"></span></label> <div abria-label="Info for context store" class="form-row form-tips node-help" style="width:100%"> <i class="fa fa-exclamation-circle fa-fw"></i><b>Force Token Refresh</b> - <span data-i18n="oauth2.tips.force"></span> </div> </div> <!-- node-client_credentials_in_body --> <div class="form-row" id="node-client_credentials_in_body"> <input type="checkbox" id="node-input-client_credentials_in_body" autocomplete="on" style="display: inline-block; width: auto; vertical-align: top;" checked /> <label for="node-input-client_credentials_in_body" style="width:auto;"><i class="fa fa-sign-in fa-fw"></i> <span data-i18n="oauth2.label.client_credentials_in_body"></span></label> <div abria-label="Info for context store" class="form-row form-tips node-help" style="width:100%"> <i class="fa fa-exclamation-circle fa-fw"></i><b>Embedded Credentials</b> - <span data-i18n="oauth2.tips.client_credentials_in_body"></span> </div> </div> <!-- node-rejectUnauthorized --> <div class="form-row" id="node-rejectUnauthorized"> <input type="checkbox" id="node-input-rejectUnauthorized" autocomplete="on" style="display: inline-block; width: auto; vertical-align: top;" checked /> <label for="node-input-rejectUnauthorized" style="width:auto;"><i class="fa fa-sign-in fa-fw"></i> <span data-i18n="oauth2.label.rejectUnauthorized"></span></label> <div aria-label="Info for context store" class="form-row form-tips node-help" style="width:100%"> <i class="fa fa-exclamation-circle fa-fw"></i><b>Reject Unauthorized</b> - <span data-i18n="oauth2.tips.rejectUnauthorized"></span> </div> </div> <!-- node-input-headers-container --> <div class="form-row" style="margin-bottom:0;"> <label><i class="fa fa-list"></i> <span data-i18n="oauth2.placeholder.headers"></span></label> </div> <div class="form-row node-input-headers-container-row"> <ol id="node-input-headers-container"></ol> </div> </script> <script type="text/html" data-template-name="oauth2"> <!-- (restante do código HTML permanece o mesmo) --> </script> <script type="text/javascript"> (function () { var headerTypes = [ { value: 'content-type', label: 'Content-Type', hasValue: false }, { value: 'location', label: 'Location', hasValue: false }, { value: 'other', label: RED._('node-red:httpin.label.other'), hasValue: true, icon: 'red/images/typedInput/az.svg' } ]; var contentTypes = [ { value: 'application/json', label: 'application/json', hasValue: false }, { value: 'application/xml', label: 'application/xml', hasValue: false }, { value: 'text/css', label: 'text/css', hasValue: false }, { value: 'text/html', label: 'text/html', hasValue: false }, { value: 'text/plain', label: 'text/plain', hasValue: false }, { value: 'image/gif', label: 'image/gif', hasValue: false }, { value: 'image/png', label: 'image/png', hasValue: false }, { value: 'other', label: RED._('node-red:httpin.label.other'), hasValue: true, icon: 'red/images/typedInput/az.svg' } ]; RED.nodes.registerType('oauth2', { category: 'DevSecOps', color: '#fff', defaults: { name: { value: '' }, container: { value: 'oauth2Response' }, grant_type: { value: 'set_by_credentials' }, access_token_url: { value: '' }, authorization_endpoint: { value: '' }, redirect_uri: { value: '' }, open_authentication: { value: '' }, username: { value: '' }, password: { value: '' }, client_id: { value: '' }, client_secret: { value: '' }, response_type: { value: '' }, access_type: { value: '' }, refresh_token: { value: '' }, prompt: { value: '' }, scope: { value: '' }, resource: { value: '' }, state: { value: '' }, proxy: { type: 'http proxy', required: false, label: RED._('node-red:oauth2.label.proxy-config') }, debug: { value: false }, force: { value: false }, senderr: { value: false }, client_credentials_in_body: { value: false }, rejectUnauthorized: { value: true }, headers: { value: {} } }, credentials: { client_id: { type: 'text' }, client_secret: { type: 'text' }, access_token: { type: 'text' }, username: { type: 'text' }, password: { type: 'text' }, refresh_token: { type: 'text' }, expire_time: { type: 'text' }, open_authentication: { type: 'text' } }, inputs: 1, outputs: 1, icon: 'oauth2.svg', label: function () { return this.name || this._('oauth2.oauth2'); }, labelStyle: function () { return this.name ? 'node_label_italic' : ''; }, oneditprepare: function () { const id = this.id; var pathname = document.location.pathname; if (pathname.slice(-1) !== '/') { pathname += '/'; } const privateIPRegex = /(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)/; let callback; if (privateIPRegex.test(location.hostname)) { const dummyDomain = 'node-red.example.com'; const actualIP = location.hostname; callback = `${location.protocol}//${dummyDomain}${location.port ? ':' + location.port : ''}${pathname}oauth2/auth/callback`; } else { 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'); } $('#node-input-container').typedInput({ default: 'msg', types: ['msg'] }); const elementMapping = { refresh_token: ['#node-access_token_url', '#node-client_id', '#node-client_secret', '#node-refresh_token', '#node-scope', '#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[grantType].forEach((selector) => $(selector).show()); RED.tray.resize(); } $('#node-input-grant_type').on('change', updateVisibility); updateVisibility(); function pollCredentials() { $.getJSON('oauth2/credentials/' + id, function (data) { if (data) { $('#node-input-open_authentication').val(data.code); delete window.configNodeIntervalId; } else { window.configNodeIntervalId = window.setTimeout(pollCredentials, 1000); } }); } $('#authorizeButton').mousedown(function () { const authorizationEndpoint = $('#node-input-authorization_endpoint').val(); const clientId = $('#node-input-client_id').val(); const proxy = $('#node-input-proxy').val(); 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'); 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); }); $('#authorizeButton').click(function (e) { const clientId = $('#node-input-client_id').val(); const clientSecret = $('#node-input-client_secret').val(); if (clientId === '' || clientSecret === '') { e.preventDefault(); } }); $('#node-input-useProxy').on('click', updateProxyOptions); function updateProxyOptions() { if ($('#node-input-useProxy').is(':checked')) { $('#node-input-useProxy-row').show(); } else { $('#node-input-useProxy-row').hide(); } RED.tray.resize(); } if (this.proxy) { $('#node-input-useProxy').prop('checked', true); } else { $('#node-input-useProxy').prop('checked', false); } updateProxyOptions(); const headerList = $('#node-input-headers-container') .css('min-height', '150px') .css('min-width', '450px') .editableList({ addItem: function (container, i, header) { 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 }); 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' }] }); 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); if (matchedContentType.length === 0) { propertyValue.typedInput('type', 'other'); propertyValue.typedInput('value', header.v); } else { propertyValue.typedInput('type', header.v); } } else { propertyValue.typedInput('value', header.v); } } propertyName.on('change', function () { const type = propertyName.typedInput('type'); if (type === 'content-type') { propertyValue.typedInput('types', contentTypes); } else { propertyValue.typedInput('types', [{ value: 'other', label: RED._('node-red:httpin.label.other'), hasValue: true, icon: 'red/images/typedInput/az.svg' }]); } RED.tray.resize(); }); RED.tray.resize(); }, removable: true }); if (this.headers) { for (const key in this.headers) { if (this.headers.hasOwnProperty(key)) { headerList.editableList('addItem', { h: key, v: this.headers[key] }); } } } }, oneditsave: function () { if (!$('#node-input-useProxy').is(':checked')) { $('#node-input-proxy').val('_ADD_'); } 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) ); }, oneditresize: function (size) { const dlg = $('#dialog-form'); const expandRow = dlg.find('.node-input-headers-container-row'); let height = dlg.height() - 5; if (expandRow && expandRow.length) { const siblingRows = dlg.find('> .form-row:not(.node-input-headers-container-row)'); for (let i = 0; i < siblingRows.size(); i++) { const cr = $(siblingRows[i]); if (cr.is(':visible')) height -= cr.outerHeight(true); } $('#node-input-headers-container').editableList('height', height); } } }); })(); </script>