https://developers.google.com/actions/reference/nodejs/ApiAiApp
I'd like to obtain an access token on Dialogflow by referring to the above official website, but the execution result of DialogflowApp.getUser() will be null.
Account linking is set up and on the client side it is certified with a Google Account.
AssistantToAgentDebug.assistantToAgentJson.user on the DEBUG tab of DialogFlow Simulator contains a value.
Also, I can get an access token by referring req.body.originalRequest.data.user.accessToken variable on the request body.
I'd like to obtain user information with DialogflowApp.getUser(),
Is there a mistake in the definition below?
*Using the Fullfilment, the logic is described in index.js.
*index.js
'use strict';
const App = require('actions-on-google').DialogflowApp;
exports.testMethod = (req, res) => {
// result is null
const app = new App({req, res});
let user = app.getUser();
console.log('User is ' + user);
// Access token can be acquired
let accessToken = req.body.originalRequest.data.user.accessToken;
console.log('accessToken is ' + accessToken);
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ 'speech': 'success!', 'displayText': 'success!' }));
};
*package.json
{
"name": "testMethod",
"engines": {
"node": "~4.2"
},
"dependencies": {
"actions-on-google": "^1.0.0",
"firebase-admin": "^4.2.1",
"firebase-functions": "^0.5.7"
}
}
The problem is that the call to the constructor expect the parameters to be named request and response. So you should write the line as
const app = new App({request:req, response:res});
You need to use getUser().accesToken like this:
let accestoken = app.getUser().accessToken;
Related
I developed a bare bones express app to test auth with Dynamics Business Central and ADAL in NodeJS. I'm getting the following 401 error. The authentication works as expected in Postman and I'm able to call the Dynamics REST endpoint in that context. In the JavaScript below I am using the same AAD tenant, client id, and client secret in Postman, but I'm not able to authenticate.
Compared the auth tokens given over Postman and in NodeJs using https://jwt.io/ and the only difference is the header values and the uti in the payload.
When I hit me getcompanies route I get the following error. I've listed my node package versions at the bottom of the post.
Error { error: { code: '401', message: 'Unauthorized' } }
Source code
var AuthenticationContext = require('adal-node').AuthenticationContext;
var crypto = require('crypto');
var express = require('express');
var request = require('request');
require('dotenv').config()
var clientId = process.env.CLIENT_ID;
var clientSecret = process.env.CLIENT_SECRET;
var authorityHostUrl = 'https://login.windows.net';
var azureAdTenant = 'grdegr.onmicrosoft.com';
var dynBusinessCentralCommonEndpoint = 'https://api.businesscentral.dynamics.com/v1.0/' + azureAdTenant + '/api/beta';
var bcRedirectUri = 'http://localhost:1337/getbctoken';
var dynBusinessCentralAuthUrl = authorityHostUrl + '/' +
azureAdTenant +
'/oauth2/authorize?response_type=code&client_id=' +
clientId +
'&redirect_uri=' +
bcRedirectUri +
'&state=<state>&resource=' +
'https://api.businesscentral.dynamics.com';
var app = express();
var port = 1337;
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
app.get('/bcauth', function(req, res) {
crypto.randomBytes(48, function(ex, buf) {
var bcToken = buf.toString('base64').replace(/\//g,'_').replace(/\+/g,'-');
res.cookie('bcauthstate', bcToken);
var dynBusinessCentralAuthUrlauthorizationUrl = dynBusinessCentralAuthUrl.replace('<state>', bcToken);
console.log('redirecting to auth url: ' + dynBusinessCentralAuthUrlauthorizationUrl);
res.redirect(dynBusinessCentralAuthUrlauthorizationUrl);
});
});
var bcAccessToken = '';
app.get('/getbctoken', function(req, res) {
var authorityUrl = authorityHostUrl + '/' + azureAdTenant;
var authenticationContext = new AuthenticationContext(authorityUrl);
console.log('getting bc auth context');
authenticationContext.acquireTokenWithAuthorizationCode(
req.query.code,
bcRedirectUri,
'https://api.businesscentral.dynamics.com/',
clientId,
clientSecret,
function(err, response) {
var message = '';
if (err) {
message = 'error: ' + err.message + '\n';
return res.send(message)
}
bcAccessToken = response.accessToken;
console.log('bc token\n' + bcAccessToken);
res.send('bc access token updated');
}
);
});
app.get('/getcompanies', (req, res) => {
var body = '';
var options = {
url: 'https://api.businesscentral.dynamics.com/v1.0/grdegr.onmicrosoft.com/api/beta/companies',
method: 'GET',
headers: {
Authorization: 'Bearer ' + bcAccessToken
},
json: JSON.stringify(body)
};
request(options, (err, response, body) => {
res.send(response || err);
if (response) {
console.log(body);
}
else {
console.log('response is null');
}
});
});
Node Packages
"devDependencies": {
"adal-node": "^0.1.28",
"request": "^2.87.0",
"webpack": "^4.12.0",
"webpack-cli": "^3.0.8"
},
"dependencies": {
"dotenv": "^6.1.0"
}
Some services are very strict when checking the aud (audience) value of an access token. Dynamics 365 Business Central expects the access token audience to be exactly https://api.businesscentral.dynamics.com. In your code, you are asking for, and getting an access token for https://api.businesscentral.dynamics.com/. That trailing slash at the end is what is making Dynamics 365 reject your access token invalid.
Change the token request to:
authenticationContext.acquireTokenWithAuthorizationCode(
req.query.code,
bcRedirectUri,
'https://api.businesscentral.dynamics.com', // <-- No trailing slash!
clientId,
clientSecret,
// ...
...and it should work.
However, there are two important things to note in your sample:
The pattern you are following is a bit strange, though it may be because you're in the early stages of development, or because it was just a minimal repro example for this question. You should not store an access token that way, because the next person who calls /getcompanies will be able to do so calling on behalf of the user who originally signed in, instead of signing in themselves. If you are looking to have users sign in with Azure AD and as part of that, call Dynamics 365 on behalf of the signed-in user, I suggest looking at passport-azure-ad.
Especially if you plan to have a system-wide account or access token, be very careful returning the original response to the end user. This is true even when developing, since it's very easy to overlook something like that when moving to production, and exposing what could be a very privileged access token to an unauthorized user.
when i use Twilio to send SMS it return
TypeError: self._qs.unescape is not a function
packaje.json :
"dependencies": {
"twilio": "^2.11.1",
"typescript": "^2.8.3",
},
"devDependencies": {
"#types/lodash": "^4.14.108",
"#types/node": "^9.6.6",
"#types/twilio": "0.0.9"
}
And My Code :
var twilio = require('twilio');
var ACCOUNT_SID = "***";
var AUTH_TOKEN = "***";
TwilioClient.messages.create({
body: 'Welcome ',
to: '+*****'
from: '+****' // From a valid Twilio number
}).then((message) => console.log(message.sid));
and returned :
Exception in delivering result of invoking 'Register': TypeError: self._qs.unescape is not a function
you need to initiate the twilio client and then use it :
var client = new twilio(ACCOUNT_SID, AUTH_TOKEN);
client.messages.create({
body: 'Welcome ',
to: '+*****'
from: '+****' // From a valid Twilio number
}).then((message) => console.log(message.sid));
In Meteor, I should connect to the Twilio API from the server.
When I requested from the client, I got the error.
I'm trying to create JWT tokens in node.js for use with the REST api in firebase, but when I try to use them, I get the error "Error: Invalid claim 'kid' in auth header."
This is my code
http.createServer(function (req, res) {
var payload = {
uid: "bruh"
};
var token = jwt.sign(payload, sact["private_key"], {
algorithm: 'RS256',
issuer: sact["client_email"],
subject: sact["client_email"],
audience: 'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit',
expiresIn: '3600s',
jwtid: sact["private_key_id"],
header: {
"kid": sact["private_key_id"]
}
});
res.writeHead(200);
res.end("It worked. (" + token + ")");
}).listen(port);
These are my requires
var http = require('http');
var jwt = require('jsonwebtoken');
Please use returnSecureToken: true, with correct Spellings
I hope it will solve the problem of Invalid claim 'kid' in the auth header.
This is an issue because you're generating a Firebase ID token, not an access token for the Firebase REST API.
To generate a REST API token I would use the legacy Firebase Token Generator library which still works perfectly well (but only generates REST tokens, not general purpose access tokens).
Note that your Firebase Database secret is now located under the gear icon in the top left of the console.
So I had this error and I've fixed it. Now here is the solution:
You'll need to retrieve the ID-token using an additional function. Here is the function you can use:
firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
// Send token to your backend via HTTPS
// ...
}).catch(function(error) {
// Handle error
});
I implemented it somewhat like this:
//google OAuth login handler
const googleLoginHandler = () => {
const provider = new firebase.auth.GoogleAuthProvider();
firebase.auth()
.signInWithPopup(provider)
.then((result) => {
/** #type {firebase.auth.OAuthCredential} */
setgoogleAuthStatus(true)
// The signed-in user info.
const userId = result.user.uid;
const displayName = result.user.displayName;
const email = result.user.email;
//This is the function for getting the ID-Token
firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then((idToken) => {
// Send token to your backend via HTTPS
console.log(idToken)
}).catch((error) => {
// Handle error
console.log(error.message)
alert(error.message)
});
console.log(result)
}).catch((error) => {
console.log(error)
// Handle Errors here.
alert(error.message)
})
}
The id token you get by this method can be used to access the firebase real-time database and other firebase services.
check out these links for more details:
https://firebase.google.com/docs/auth/admin/verify-id-tokens#retrieve_id_tokens_on_clients
https://firebase.google.com/docs/database/rest/auth#firebase_id_tokens
I am trying to use the Object Storage Service at IBM Bluemix Cloud, but I can't send images from my nodejs server. How can I do this? Follow my server code:
unirest
.post(MY_CONTAINER + new_fname)
.headers({'Content-Type': 'multipart/form-data', 'X-Auth-Token': token})
.field({ 'max_file_count': 1 })
.field({ 'max_file_size': 1 })
.attach({ 'file': file.originalname, 'relative file': streamFile })
.end(function (resp) {
//response
console.log(resp.status);
console.log(resp.body);
});
The main problem is to find the right way to send an image (png or jpg) to the bluemix storage using the API (I've aready uploaded it to our server).
I used the pkgcloud-bluemix-objectstorage to fix the OpenStack authentication bug that previously used the v2 and has been changed to use the v3.
here's the link
Bluemix - object storage - node.js - pkgcloud - openstack returns 401
the #libik writes an example.
var pkgcloud = require('pkgcloud-bluemix-objectstorage');
var fs = require('fs');
// Create a config object
var config = {};
// Specify Openstack as the provider
config.provider = "openstack";
// Authentication url
config.authUrl = 'https://identity.open.softlayer.com/';
config.region= 'dallas';
// Use the service catalog
config.useServiceCatalog = true;
// true for applications running inside Bluemix, otherwise false
config.useInternal = false;
// projectId as provided in your Service Credentials
config.tenantId = '234567890-0987654';
// userId as provided in your Service Credentials
config.userId = '098765434567890';
// username as provided in your Service Credentials
config.username = 'admin_34567890-09876543';
// password as provided in your Service Credentials
config.password = 'sdfghjklkjhgfds';
**//This is part which is NOT in original pkgcloud. This is how it works with newest version of bluemix and pkgcloud at 22.12.2015.
//In reality, anything you put in this config.auth will be send in body to server, so if you need change anything to make it work, you can. PS : Yes, these are the same credentials as you put to config before.
//I do not fill this automatically to make it transparent.
config.auth = {
forceUri : "https://identity.open.softlayer.com/v3/auth/tokens", //force uri to v3, usually you take the baseurl for authentication and add this to it /v3/auth/tokens (at least in bluemix)
interfaceName : "public", //use public for apps outside bluemix and internal for apps inside bluemix. There is also admin interface, I personally do not know, what it is for.
"identity": {
"methods": [
"password"
],
"password": {
"user": {
"id": "098765434567890", //userId
"password": "sdfghjklkjhgfds" //userPassword
}
}
},
"scope": {
"project": {
"id": "234567890-0987654" //projectId
}
}
};**
//console.log("config: " + JSON.stringify(config));
// Create a pkgcloud storage client
var storageClient = pkgcloud.storage.createClient(config);
// Authenticate to OpenStack
storageClient.auth(function (error) {
if (error) {
console.error("storageClient.auth() : error creating storage client: ", error);
} else {
//OK
var new_fname = dir + "__" + file.originalname;
var readStream = fs.createReadStream('uploads/' + file.filename);
var writeStream = storageClient.upload({
container: 'chat-files',
remote: new_fname
});
writeStream.on('error', function(err) {
// handle your error case
console.log("concluido o upload com erro!");
console.log(err);
});
writeStream.on('success', function(file) {
// success, file will be a File model
console.log("concluido o upload com sucesso!");
});
readStream.pipe(writeStream);
}
});
#JeffSloyer wrote a Node.js sample application to upload files to an Object Storage instance in Bluemix.
You can find the code here:
https://github.com/IBM-Bluemix/node-file-upload-swift
The code above fails to authenticate using Open Stack Swift v3, so I made a modification to the skipper-openstack module to use pkgcloud-bluemix-objectstorage:
https://github.com/adasilva70/skipper-openstack.git#adasilva70-patch-1
Clone Jeff's repository and follow the instructions in the README.md file to run the code. Make sure you modify the package.json file with the one below to get my changes:
{
"name": "node-file-upload-swift",
"version": "0.0.0",
"dependencies": {
"bower": "^1.7.1",
"cf-deployment-tracker-client": "*",
"cfenv": "^1.0.3",
"dotenv": "^1.2.0",
"express": "~4.x",
"skipper": "^0.5.8",
"skipper-openstack": "git+https://github.com/adasilva70/skipper-openstack.git#adasilva70-patch-1",
"stream": "0.0.2",
"underscore": "^1.8.3"
},
"main": "server.js",
"scripts": {
"start": "node server.js",
"postinstall": "bower install --allow-root --config.interactive=false"
}
}
I have been "playing" with authentication using node.js, express and socket.io
I have tried the example here http://howtonode.org/socket-io-auth
app.use(express.cookieParser("rocks"));
app.use(express.session());
io.set('authorization', function(handshakeData, accept) {
console.log('in authorization');
if(handshakeData.headers.cookie) {
handshakeData.cookie = cookie.parse(handshakeData.headers.cookie);
handshakeData.sessionID = connect.utils.parseSignedCookie(handshakeData.cookie['express.sid']);
if (handshakeData.cookie['express.sid'] == handshakeData.sessionID) {
return accept('Cookie is invalid.', false);
}
} else {
return accept('No cookie transmitted.', false);
}
accept(null, true);
});
I keep getting an error generated by connect, more specifically this call line
connect.utils.parseSignedCookie(handshakeData.cookie['express.sid']);
/workspace/node_modules/connect/node_modules/cookie-signature/index.js:39
if ('string' != typeof secret) throw new TypeError('secret required');
^
TypeError: secret required
I have tried to find the api documentation for express.cookieParser (which I have) and express.session (which I have not) and looked at the offending source code. I imagine there is something going on with the way I have set up the project as connect seems to have a cookieParser as well as express, or express is using the cookieParser from connect.
My package.json looks like
{
"name": "mplayer",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node app.js"
},
"dependencies": {
"socket.io":"0.9.16",
"express": "3.4.0",
"jade": "*",
"stylus": "*",
"cookie": "*"
}
}
Can someone please shed some light on why I am getting this error and secondly can someone share the most effective way of authenticating a user using Node.js, socket.io and express (I know there are various articles out there, but things seem to have moved on. It is hard to keep track of what is current).
You need to pass a secret for parsing signed cookies. This function:
connect.utils.parseSignedCookie();
Accepts two arguments, the first being req.cookies, the second being the secret. This is how you need to use the function:
var signedCookies = connect.utils.parseSignedCookie(handshake.headers.cookie, 'secret');
// signedCookies['express.sid']
The reason you're getting the error, is that you aren't passing a string as the second argument, which is what fails the typeof 'string' check.
As for the most effective way of authenticating Socket.IO users when used in conjunction with Express, it is already the way you're already using it.
var cookieParser = require('cookie-parser');
var cookie = require('cookie');
var secret = config.get('session:secret');
io.set('authorization', function(handshake, callback){
async.waterfall([
function(callback) {
handshake.cookies = cookie.parse(handshake.headers.cookie || '');
var sidCookie = handshake.cookies[config.get('session:key')];
var sid = cookieParser.signedCookie(sidCookie, secret);
}
});
https://www.npmjs.com/package/cookie-parser#cookieparser-signedcookie-str-secret-