diff --git a/README.md b/README.md index 80de688..fc20d48 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,8 @@ -# Generic Google API Client for Node-RED +# Generic Google API Client for Node-RED using OAuth2 -Node-RED node for Google APIs. +Node-RED node for Google APIs. -## Work in Progress - -Changes are coming. - -Configuration node name was changed at v.0.1.0: _google conn_ -> _google-conn_ +This Node is based on the implementation by [74Labs](https://github.com/74Labs/node-red-contrib-google). It has been updated to use the latest version of the __googleapis__. Further the authorization workflows has been changed to __OAuth2__. ## Features @@ -14,25 +10,27 @@ This node is a wrapper for official Google APIs Node.js Client: [google-api-node List of available APIs are delivered online via [Google API Discovery Service](https://developers.google.com/discovery/). -Package contains two nodes. There is configuration node made for maintaining connection to Google API Services (_google-conn_) and regular node providing posibility to call any method of any API exposed via official Google's Node.js Client. +Package contains two nodes. There is configuration node made for maintaining connection to Google API Services (_google-credentials_) and regular node providing posibility to call any method of any API exposed via official Google's Node.js Client. ## How to Install Run the following command in the root directory of your Node-RED install ``` -npm install node-red-contrib-google +npm install node-red-contrib-google-oauth2 ``` or for a global installation ``` -npm install -g node-red-contrib-google +npm install -g node-red-contrib-google-oauth2 ``` ## Configuration -1. Generate service account key at [Google API Console](https://console.developers.google.com/apis/credentials/serviceaccountkey). +1. Generate OAuth credentials at [Google API Console](https://console.developers.google.com/apis/credentials/oauthclient). + + * Choose Web Application. + * As `Authorized JavaScript origins` enter your Node-RED IP (_e.g. `http://localhost:1880`_) + * As `Authorized redirect URIs` enter your Node-RED IP plus `/google-credentials/auth/callback` (_e.g. `http://localhost:1880/google-credentials/auth/callback`_) - * Choose JSON type and save service key file. - * Paste content of that file into JSON Key field of your _google-conn_ node. - +2. Copy the `Client ID` and `Client secret` and paste them into the Config Node diff --git a/google-auth.html b/google-auth.html new file mode 100644 index 0000000..43d99b5 --- /dev/null +++ b/google-auth.html @@ -0,0 +1,137 @@ + + + diff --git a/google-auth.js b/google-auth.js new file mode 100644 index 0000000..09457a6 --- /dev/null +++ b/google-auth.js @@ -0,0 +1,98 @@ +module.exports = function(RED) { + "use strict"; + const crypto = require("crypto"); + const url = require('url'); + const { google } = require('googleapis'); + + function GoogleNode(n) { + RED.nodes.createNode(this,n); + this.displayName = n.displayName; + this.scopes = n.scopes; + } + RED.nodes.registerType("google-credentials",GoogleNode,{ + credentials: { + displayName: {type:"text"}, + clientId: {type:"text"}, + clientSecret: {type:"password"}, + accessToken: {type:"password"}, + refreshToken: {type:"password"}, + expireTime: {type:"password"} + } + }); + + RED.httpAdmin.get('/google-credentials/auth', function(req, res){ + console.log('google-credentials/auth'); + if (!req.query.clientId || !req.query.clientSecret || + !req.query.id || !req.query.callback) { + res.send(400); + return; + } + const node_id = req.query.id; + const callback = req.query.callback; + const credentials = { + clientId: req.query.clientId, + clientSecret: req.query.clientSecret + }; + const scopes = req.query.scopes; + + const csrfToken = crypto.randomBytes(18).toString('base64').replace(/\//g, '-').replace(/\+/g, '_'); + credentials.csrfToken = csrfToken; + credentials.callback = callback; + res.cookie('csrf', csrfToken); + res.redirect(url.format({ + protocol: 'https', + hostname: 'accounts.google.com', + pathname: '/o/oauth2/auth', + query: { + access_type: 'offline', + approval_prompt: 'force', + scope: scopes, + response_type: 'code', + client_id: credentials.clientId, + redirect_uri: callback, + state: node_id + ":" + csrfToken, + } + })); + RED.nodes.addCredentials(node_id, credentials); + }); + + RED.httpAdmin.get('/google-credentials/auth/callback', function(req, res) { + console.log('google-credentials/auth/callback'); + if (req.query.error) { + return res.send("google.error.error", {error: req.query.error, description: req.query.error_description}); + } + var state = req.query.state.split(':'); + var node_id = state[0]; + var credentials = RED.nodes.getCredentials(node_id); + if (!credentials || !credentials.clientId || !credentials.clientSecret) { + console.log("credentials not present?"); + return res.send("google.error.no-credentials"); + } + if (state[1] !== credentials.csrfToken) { + return res.status(401).send("google.error.token-mismatch"); + } + + const oauth2Client = new google.auth.OAuth2( + credentials.clientId, + credentials.clientSecret, + credentials.callback + ); + + oauth2Client.getToken(req.query.code) + .then((value) => { + credentials.accessToken = value.tokens.access_token; + credentials.refreshToken = value.tokens.refresh_token; + credentials.expireTime = value.tokens.expiry_date; + credentials.tokenType = value.tokens.token_type; + credentials.displayName = value.tokens.scope.substr(0, 40); + + delete credentials.csrfToken; + delete credentials.callback; + RED.nodes.addCredentials(node_id, credentials); + res.send('Authorized'); + }) + .catch((error) => { + return res.send('Could not receive tokens'); + }); + }); +}; \ No newline at end of file diff --git a/google.html b/google.html index da484d7..994033b 100644 --- a/google.html +++ b/google.html @@ -1,49 +1,3 @@ - - - -