Actions on Google 'Get Sign In' Helper - oauth-2.0

I am using the following code in my Actions on Google application:
app.intent('Get Sign In', async (conv, params, signin) => {
if (signin.status !== 'OK') {
return conv.close(`Let's try again next time.`);
}
const color = conv.data[Fields.COLOR];
const {email} = conv.user;
if (!conv.data.uid && email) {
try {
conv.data.uid = (await auth.getUserByEmail(email)).uid;
} catch (e) {
if (e.code !== 'auth/user-not-found') {
throw e;
}
// If the user is not found, create a new Firebase auth user
// using the email obtained from the Google Assistant
conv.data.uid = (await auth.createUser({email})).uid;
}
}
if (conv.data.uid) {
conv.user.ref = db.collection('users').doc(conv.data.uid);
}
conv.close(`I saved ${color} as your favorite color for next time.`);
});
I've been looking through the docs, but can't find any explanation about the 'params' argument of the function? How do I pass values to it?

You can use the params object if you are using dialogflow. For example, if you use entities in Dialogflow you can retrieve them through params object in the intent handler.
If you are using Dialogflow, you can access the parameter values via the params variable.
Source
More info about the param object itself can be found here.

Related

Pagination - How can I store and provide a non-numeric page identifier?

I am trying to add pagination to my Zapier trigger.
The API I am using for the trigger supports pagination, but not using a page number in the traditional sense (ie. page 1,2,3,...). Instead, the API response includes a key (ie. "q1w2e3r4") which should be passed as a parameter to the next request to get the next page of results.
From looking at the docs, I can use {{bundle.meta.page}} (which defaults to 0 unless otherwise set).
I am trying to set {{bundle.meta.page}} in the code editor, with an example shown below:
const options = {
url: 'company_xyz.com/api/widgets',
method: 'GET',
...,
params: {
...,
'pagination_key': bundle.meta.page,
}
}
return z.request(options)
.then((response) => {
response.throwForStatus();
const json_response = response.json;
widgets = json_response.widgets
...
bundle.meta.page = json_response["next_pagination_key"]
return widgets;
});
The problem is that when Zapier tries to retrieve the next page, bundle.meta.page will be 1 instead of the value of "next_pagination_key" from the result of the previous request.
There are docs on cursor-based pagination in the CLI docs.
The relevant block is:
const performWithAsync = async (z, bundle) => {
let cursor;
if (bundle.meta.page) {
cursor = await z.cursor.get(); // string | null
}
const response = await z.request(
'https://5ae7ad3547436a00143e104d.mockapi.io/api/recipes',
{
// if cursor is null, it's sent as an empty query
// param and should be ignored by the server
params: { cursor: cursor }
}
);
// we successfully got page 1, should store the cursor in case the user wants page 2
await z.cursor.set(response.nextPage);
return response.items;
};
This should work in the Zapier Visual Builder, but you might need to use the CLI instead. You can export your integration using the zapier convert CLI command (docs).

Is possible to automate OAuth 2.0 implicit grant flow of Azure active directory in postman?

I'm trying to automate all the testing of an API. Currently is using a utentificacion using AAD.
The problem is: I can use the process of postman to get the token using OAuth2.0
Postman dialog
but I can't run a collection and do something like a trigger to get the token at the beginning. If i want to take the token I must push the button "Get new access token"
there is some way to do it automatically? or how can I create a flow to obtain the token?
Thanks!
You could use Pre-request Script to do it automatically. You just need to modify your required value and post it in the Pre-request Script of the postman. It had better in the parent collection, so it could inherit auth from the parent.
var getToken = true;
if (!pm.environment.get('accessTokenExpiry') ||
!pm.environment.get('currentAccessToken')) {
console.log('Token or expiry date are missing')
} else if (pm.environment.get('accessTokenExpiry') <= (new Date()).getTime()) {
console.log('Token is expired')
} else {
getToken = false;
console.log('Token and expiry date are all good');
}
if (getToken === true) {
pm.sendRequest({
url: 'https://login.microsoftonline.com/microsoft.onmicrosoft.com/oauth2/token',
method: 'POST',
header: 'Content-Type:application/x-www-form-urlencoded',
body: {
mode: 'raw',
raw: 'grant_type=implicit&client_id...'
}
}, function (err, res) {
console.log(err ? err : res.json());
if (err === null) {
console.log('Saving the token and expiry date')
var responseJson = res.json();
pm.environment.set('currentAccessToken', responseJson.access_token)
var expiryDate = new Date();
expiryDate.setSeconds(expiryDate.getSeconds() + responseJson.expires_in);
pm.environment.set('accessTokenExpiry', expiryDate.getTime());
}
});
}
For the code sample, you could refer to here.

Zapier JS Action to Fetch Klout Scores

I'm trying to create a Java Script Code Action on Zapier to fetch Klout Scores for any given Twitter user name...
I've realized that this needs to be done in 2 stages:
1) First get the Klout ID for any Twitter screen_name:
http://api.klout.com/v2/identity.json/twitter?screenName="+screen_name+"&key="+klout_apikey"
Klout replies back to that with JSon:
{"id":"85568398087870011","network":"ks"}
2) second get the Klout score for that Klout id:
http://api.klout.com/v2/user.json/"+klout.id+"/score?key="+klout_apikey"
Klout replies back to this with JSon:
{"score":65.68382904221806,"scoreDelta":{"dayChange":-0.03663891859041257,"weekChange":-0.5495711661078815,"monthChange":-1.4045672671990417},"bucket":"60-69"}
Of course, what I need is the "score":65.68382904221806 object of the JSon reply array.
I use these following JS functions proposed by #KayCee:
var klout_apikey = '<my klout api key>';
fetch("http://api.klout.com/v2/identity.json/twitter?screenName="+screen_name+"&key="+klout_apikey")
.then(function(res) {
return res.json();
})
.then(function(klout) {
console.log(klout);
if(klout.id) {
return fetch("http://api.klout.com/v2/user.json/"+klout.id+"/score?key="+klout_apikey")
}
}).then(function(res) {
return res.json();
}).then(function(body) {
// console.log(body.score);
//Here is where you are telling Zapier what you want to output.
callback(null, body.score)
}).catch(callback); //Required by Zapier for all asynchronous functions.
In the "input data" section of the Zapier code action i pass the screen_name as a variable:
screen_name: [the twitter handle]
What I get back is the following error message:
SyntaxError: Invalid or unexpected token
What is the error that you see? You could do this by simply using the fetch client. You might want to remove the variable declarations before adding this to the code step.
var inputData = {'screen_name': 'jtimberlake'}
//Remove the line above before pasting in the Code step. You will need to configure it in the Zap.
var klout_apikey = '2gm5rt3hsdsdrzgvnskmgm'; //Not a real key
fetch("http://api.klout.com/v2/identity.json/twitter?screenName="+inputData.screen_name+"&key="+klout_apikey)
.then(function(res) {
return res.json();
})
.then(function(body) {
console.log(body);
if(body.id) {
return fetch("http://api.klout.com/v2/user.json/"+body.id+"/score?key="+klout_apikey)
}
}).then(function(res) {
return res.json();
}).then(function(body) {
console.log(body);
//Here is where you are telling Zapier what you want to output.
callback(null, body)
}).catch(callback); //Required by Zapier for all asynchronous functions.
Refer to their documentation here - https://zapier.com/help/code/#introductory-http-example
Also refer to their Store client which allows you to store values (for cache) - https://zapier.com/help/code/#storeclient-javascript

Custom authentication integration with parse-server and auth0

I would like to use auth0.com in conjunction with the open source-parse server.
My current approach is to obtain the token from auth0 by using their standard login through the Lock library for iOS. With that token I would like to call a custom authentication method on my parse-server, that checks whether the token is valid and if it is will log in the user.
My problem is that there is almost no documentation on writing custom oauth for parse-server.
So far, I have this code for my custom auth.
var Parse = require('parse/node').Parse;
function validateAuthData(authData, options) {
console.log('validateAuthData()');
return new Promise((resolve, reject) => {
try {
var decoded = jwt.verify(authData.access_token, opions.sharedSecret);
if (authData.id === decoded.sub) {
resolve({});
}
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Unauthorized');
} catch(e) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, e.message);
}
});
}
function validateAppId(appIds, authData) {
console.log('validateAppId()');
return Promise.resolve();
}
module.exports = {
validateAppId: validateAppId,
validateAuthData: validateAuthData
};
However, it doesn't work and also I don't understand how this code can be used to authenticate a specific user. Does the parse-server do database look-ups to match the specific auth data to a specific user? Also, how can I register a new user with custom auth. What happens when a user tries to log in but he doesn't exist yet in my parse database?
An alternative seems to be this, using a rule an auth0.com. What are the differences and how would the rule work? I have very little experience with authentication and oauth and jwt's.
Lastly, I am using this to call my custom auth from my iOS client. However this doesn't work either, but I am not sure whether it is due to the iOS part or because my custom auth isn't working yet.
In conclusion, I am having trouble with something that seems rather easy. I want to use auth0 as my authentication provider and I want to integrate it was the parse-server, since I really appreciate the convenience around parse and the client sdk's. I am fairly certain that more people have a similar problem, however I have not found any definitive resource on how to properly do this.
Further Links
Parse user authenticated using Auth0
https://auth0.com/blog/2016/03/07/hapijs-authentication-secure-your-api-with-json-web-tokens/
https://github.com/ParsePlatform/parse-server/wiki/OAuth
https://jwt.io/introduction/
late answer but I was solving the same problem and came across this post:
Auth0 has rules you can apply that run when the login occurs. I've modified their example one from https://github.com/auth0/rules/blob/master/src/rules/parse.js, extracting the API endpoint into a constant.
function(user, context, callback) {
// run this only for the Parse application
// if (context.clientID !== 'PARSE CLIENT ID IN AUTH0') return callback(null, user, context);
const request = require('request');
const MY_API = 'https://subdomian.back4app.io';
const PARSE_APP_ID = '*********';
const PARSE_API_KEY = '**********';
const PARSE_USER_PASSWORD = 'REPLACE_WITH_RANDOM_STRING'; // you can use this to generate one http://www.random.org/strings/
const username = user.email || user.name || user.user_id; // this is the Auth0 user prop that will be mapped to the username in the db
request.get({
url: `${MY_API}/login`,
qs: {
username: username,
password: PARSE_USER_PASSWORD
},
headers: {
'X-Parse-Application-Id': PARSE_APP_ID,
'X-Parse-REST-API-Key': PARSE_API_KEY
}
},
function(err, response, body) {
if (err) return callback(err);
// user was found, add sessionToken to user profile
if (response.statusCode === 200) {
context.idToken[`${MY_API}/parse_session_token`] = JSON.parse(body).sessionToken;
return callback(null, user, context);
}
// Not found. Likely the user doesn't exist, we provision one
if (response.statusCode === 404) {
request.post({
url: `${MY_API}/users`,
json: {
username: username,
password: PARSE_USER_PASSWORD
},
headers: {
'X-Parse-Application-Id': PARSE_APP_ID,
'X-Parse-REST-API-Key': PARSE_API_KEY,
'Content-Type': 'application/json'
}
},
function(err, response, body) {
if (err) return callback(new Error('user already exists'));
// user created, add sessionToken to user profile
if (response.statusCode === 201) {
context.idToken[`${MY_API}/parse_session_token`] = body.sessionToken;
return callback(null, user, context);
}
return callback(new Error(username + ' The user provisioning returned an unknown error. Body: ' + JSON.stringify(body)));
});
} else {
return callback(new Error('The login returned an unknown error. Status: ' + response.statusCode + ' Body: ' + body));
}
});
}
I'm writing a SPA in JS, so I have some client side code that handles the Auth0 login, (replace 'https://subdomian.back4app.io' with your own parse server's API address - the same value as used in the above Auth0 rule). Note the Parse.User.become function, which assigns the session id created in the Auth0 rule to the current parse User:
handleAuthentication() {
this.auth0.parseHash((err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
this.setSession(authResult);
Parse.User.become(authResult.idTokenPayload['https://subdomian.back4app.io/parse_session_token']);
history.replace('/');
} else if (err) {
history.replace('/home');
console.log(err);
}
});
}

How can I login to Meteor with native device Facebook?

Suppose I logged into my device's Facebook authentication, like system Facebook on iOS. I obtain an access token.
How can I use the access token to login to Meteor's Facebook Oauth provider?
To login with Facebook using an access token obtained by another means, like iOS Facebook SDK, define a method on the server that calls the appropriate Accounts method:
$FB = function () {
if (Meteor.isClient) {
throw new Meteor.Error(500, "Cannot run on client.");
}
var args = Array.prototype.slice.call(arguments);
if (args.length === 0) {
return;
}
var path = args[0];
var i = 1;
// Concatenate strings together in args
while (_.isString(args[i])) {
path = path + "/" + args[i];
i++;
}
if (_.isUndefined(path)) {
throw new Meteor.Error(500, 'No Facebook API path provided.');
}
var FB = Meteor.npmRequire('fb');
var fbResponse = Meteor.sync(function (done) {
FB.napi.apply(FB, [path].concat(args.splice(i)).concat([done]));
});
if (fbResponse.error !== null) {
console.error(fbResponse.error.stack);
throw new Meteor.Error(500, "Facebook API error.", {error: fbResponse.error, request: args});
}
return fbResponse.result;
};
Meteor.methods({
/**
* Login to Meteor with a Facebook access token
* #param accessToken Your Facebook access token
* #returns {*}
*/
facebookLoginWithAccessToken: function (accessToken) {
check(accessToken, String);
var serviceData = {
accessToken: accessToken
};
// Confirm that your accessToken is you
try {
var tokenInfo = $FB('debug_token', {
input_token: accessToken,
access_token: Meteor.settings.facebook.appId + '|' + Meteor.settings.facebook.secret
});
} catch (e) {
throw new Meteor.Error(500, 'Facebook login failed. An API error occurred.');
}
if (!tokenInfo.data.is_valid) {
throw new Meteor.Error(503, 'This access token is not valid.');
}
if (tokenInfo.data.app_id !== Meteor.settings.facebook.appId) {
throw new Meteor.Error(503, 'This token is not for this app.');
}
// Force the user id to be the access token's user id
serviceData.id = tokenInfo.data.user_id;
// Returns a token you can use to login
var loginResult = Accounts.updateOrCreateUserFromExternalService('facebook', serviceData, {});
// Login the user
this.setUserId(loginResult.userId);
// Return the token and the user id
return loginResult;
}
}
This code depends on the meteorhacks:npm package. You should call meteor add meteorhacks:npm and have a package.json file with the Facebook node API: { "fb": "0.7.0" }.
If you use demeteorizer to deploy your app, you will have to edit the output package.json and set the scrumptious dependency from "0.0.1" to "0.0.0".
On the client, call the method with the appropriate parameters, and you're logged in!
In Meteor 0.8+, the result of Accounts.updateOrCreateUserFromExternalService has changed to an object containing {userId: ...} and furthermore, no longer has the stamped token.
You can get the accessToken in the Meteor.user() data at Meteor.user().services.facebook.accessToken (be aware this can only be accessed on the server side as the services field is not exposed to the client.
So when a user logs in with facebook on your meteor site these fields would be populated with the user's facebook data. If you check your meteor user's database with mongo or some other gui tool you could see all the fields which you have access to.
Building on DrPangloss' most excellent answer above, combining it with this awesome post: http://meteorhacks.com/extending-meteor-accounts.html
You'll run into some issues using ObjectiveDDP in trying to get the client persist the login. Include the header:
#import "MeteorClient+Private.h"
And manually set the required internals. Soon I'll make a meteorite package and an extension to MyMeteor (https://github.com/premosystems/MyMeteor) but for now it's manual.
loginRequest: {"accessToken":"XXXXXb3Qh6sBADEKeEkzWL2ItDon4bMl5B8WLHZCb3qfL11NR4HKo4TXZAgfXcySav5Y8mavDqZAhZCZCnDDzVbdNmaBAlVZAGENayvuyStkTYHQ554fLadKNz32Dym4wbILisPNLZBjDyZAlfSSgksZCsQFxGPlovaiOjrAFXwBYGFFZAMypT9D4qcZC6kdGH2Xb9V1yHm4h6ugXXXXXX","fbData":{"link":"https://www.facebook.com/app_scoped_user_id/10152179306019999/","id":"10152179306019999","first_name":"users' first name","name":"user's Full Name","gender":"male","last_name":"user's last name","email":"users#email.com","locale":"en_US","timezone":-5,"updated_time":"2014-01-11T23:41:29+0000","verified":true}}
Meteor.startup(
function(){
Accounts.registerLoginHandler(function(loginRequest) {
//there are multiple login handlers in meteor.
//a login request go through all these handlers to find it's login hander
//so in our login handler, we only consider login requests which has admin field
console.log('loginRequest: ' + JSON.stringify(loginRequest));
if(loginRequest.fbData == undefined) {
return undefined;
}
//our authentication logic :)
if(loginRequest.accessToken == undefined) {
return null;
} else {
// TODO: Verfiy that the token from facebook is valid...
// https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.0#checktoken
// graph.facebook.com/debug_token? input_token={token-to-inspect}&access_token={app-token-or-admin-token}
}
//we create a user if not exists, and get the userId
var email = loginRequest.fbData.email || "-" + id + "#facebook.com";
var serviceData = {
id: loginRequest.fbData.id,
accessToken: loginRequest.accessToken,
email: email
};
var options = {
profile: {
name: loginRequest.fbData.name
}
};
var user = Accounts.updateOrCreateUserFromExternalService('facebook', serviceData, options);
console.log('Logged in from facebook: ' + user.userId);
//send loggedin user's user id
return {
userId: user.userId
}
});
}
);
This answer could be improved further as we can now directly debug the token from a REST http request using futures. Credit still goes to #DoctorPangloss for the principal steps necessary.
//Roughly like this - I removed it from a try/catch
var future = new Future();
var serviceData = {
accessToken: accessToken,
email: email
};
var input = Meteor.settings.private.facebook.id + '|' + Meteor.settings.private.facebook.secret
var url = "https://graph.facebook.com/debug_token?input_token=" + accessToken + "&access_token=" + input
HTTP.call( 'GET', url, function( error, response ) {
if (error) {
future.throw(new Meteor.Error(503, 'A error validating your login has occured.'));
}
var info = response.data.data
if (!info.is_valid) {
future.throw(new Meteor.Error(503, 'This access token is not valid.'));
}
if (info.app_id !== Meteor.settings.private.facebook.id) {
future.throw(new Meteor.Error(503, 'This token is not for this app.'));
}
// Force the user id to be the access token's user id
serviceData.id = info.user_id;
// Returns a token you can use to login
var user = Accounts.updateOrCreateUserFromExternalService('facebook', serviceData, {});
if(!user.userId){
future.throw(new Meteor.Error(500, "Failed to create user"));
}
//Add email & user details if necessary
Meteor.users.update(user.userId, { $set : { fname : fname, lname : lname }})
Accounts.addEmail(user.userId, email)
//Generate your own access token!
var token = Accounts._generateStampedLoginToken()
Accounts._insertLoginToken(user.userId, token);
// Return the token and the user id
future.return({
'x-user-id' : user.userId,
'x-auth-token' : token.token
})
});
return future.wait();
Use this instead of the JS lib suggested by #DoctorPangloss. Follow the same principles he suggested but this avoids the need to integrate an additional library

Resources