Using plaid in google functions is not working - plaid

So I am using google functions to write a script to auto pay my workers! In my back end i have stored the public token and account id.. I am trying to use plaid to turn into a stripe token then use stripe to do the transfer! The stripe thing is working but the plaid functions wont return the new stripe bank account number.. any ideas?
plaidClient.exchangePublicToken("public-sandbox-6be57fb5-3286-4bc8-a770-54a16ea39283",
res => {
var accessToken = res.access_token;
// debugging = exchangedata.access_token;
//debugging = err.message;
// Generate a bank account token
plaidClient.createStripeToken(accessToken, snapshot.val().plaid_account_id,
res => {
bankAccountToken = res.stripe_bank_account_token;
stripe.transfers.create({
amount: (Number(appointmentchildSnapshot.val().price)/3).toString(),
currency: "usd",
destination:bankAccountToken,
transfer_group: "ORDER_95"
},(_err, transfer)=> {
// asynchronously called
});
});

Make you use double " " when declaring your client secret and stuff!
In order to test for errors use res.json(Error:responsetowhatfunction you are using)
-- Some bank tokens do not work with Plaid unfortunately in sandbox and they also expire pretty quickly so if its not working create a new token and try again
---With the error log stuff you can keep track of when this happens
await plaidClient.exchangePublicToken(snapshot.val().plaid_token,
async (error,response1) => {
if (error !== null) {
res.json({error:snapshot.val().plaid_token});
} else {
var accessToken = response1.access_token;
//res.json(accessToken);
// debugging = exchangedata.access_token;
//debugging = err.message;
// Generate a bank account token
debugging = await plaidClient.createStripeToken(accessToken, snapshot.val().plaid_account_id,
async (error2,response2) => {
if(error2!==null){
res.json({error:snapshot.val().plaid_account_id});
}else{
//res.json({error:response2});
// bankAccountToken = response2.stripe_bank_account_token;
stripe.transfers.create({
amount: (Number(appointmentchildSnapshot.val().price)/3).toString(),
currency: "usd",
destination:response2.stripe_bank_account_token,
transfer_group: "ORDER_95"
},(_err, transfer)=> {
// asynchronously called
});
}

Related

Stripe returns a "No such token" error (Plaid Integration)

I am getting this error from Stripe when I try to create a new source for the selected bank account. I am using the new (beta) version of the Plaid Node SDK. Here is my code:
let user;
const mode = "sandbox";
const dsService = new CaspioDsService();
// Load the user if not already loaded by cognitoAuth
if (!req.user) {
user = new User(
dsService,
new CaspioRefDataService(),
new AuthUserService({
organizationId: res.locals.organization.Organization_ID,
isAuthenticated: res.locals.isAuthenticated,
})
);
await user.load(req.params.userId);
} else {
user = req.user.userObject;
}
const configuration = new Configuration({
basePath: PlaidEnvironments[mode],
baseOptions: {
headers: {
"PLAID-CLIENT-ID": config.plaid.clientId,
"PLAID-SECRET": mode === "sandbox" ? config.plaid.secretSandbox : config.plaid.secretProduction,
"Plaid-Version": "2020-09-14",
},
},
});
const plaidClient = new PlaidApi(configuration);
console.log(configuration.basePath); // https://sandbox.plaid.com
// Exchange the public token for the Plaid access token
const plaidTokenRes = await plaidClient.itemPublicTokenExchange({
public_token: req.body.publicToken,
});
const accessToken = plaidTokenRes.data.access_token;
console.log(accessToken); // access-sandbox-d92396c2-1f49-4780-9ae9-23d50645f364
// Get the Stripe bank account token from Plaid
const stripeTokenRes = await plaidClient.processorStripeBankAccountTokenCreate({
access_token: accessToken,
account_id: req.body.accountId
});
const bankAccountToken = stripeTokenRes.data.stripe_bank_account_token;
console.log(bankAccountToken); // btok_1JFMGwGq7ejZoSiwGmM8WSSm
let stripeCustomerId = user.getStripeToken();
const stripeClient = await StripeHelper.getStripeClient(mode); // Get Stripe client in sandbox mode
console.log(stripeCustomerId); // cus_Jt7AWZjC8rHPzt
// Add the source to the Stripe customer and get the bank account info
const bankAccount = await stripeClient.customers.createSource(stripeCustomerId, {
source: bankAccountToken,
}); // Error: No such token: 'btok_1JFMGwGq7ejZoSiwGmM8WSSm'
Any ideas what I might be doing wrong? I expect the issue is with my code, or possibly Plaid (I don't think it is a Stripe problem).
It sounds like you're getting a token from Plaid, but Stripe is rejecting it, which suggests a problem with the relationship between your Plaid and Stripe setups. Are you sure that you enabled the Plaid/Stripe integration in the Plaid dashboard and that the client id / secret you're using matches the Plaid account where the integration is enabled? The Plaid docs also suggest that this error can be caused by using a mismatched set of environments (e.g. using Production with Stripe but Sandbox with Plaid).
The problem was that we had the wrong Stripe account connected. Silly one, but I'm posting this in case anyone else makes the same mistake.

Sending message from Cognito triggers

I want to restrict user sign-ins from Cognito hosted UI. I can see there are triggers in which we can attach lambda, but whenever I change event object inside of lambda, instead of getting my custom message User exceeded limits, I get unrecognizable lambda output error.
Can anyone help me in this or is there any other way to achieve this functionality?
Now,I'm getting this
with this code :
exports.handler = (event, context, callback) => {
if (true) {
var error = new Error("Cannot signin because your signin count is 5");
// Return error to Amazon Cognito
callback(error, event);
}
// Return to Amazon Cognito
callback(null, event);
};
But,I don't want prefix PreAuthentication failed with error,I just want to display my message.
Any help is appreciated.
Currently, there is no way to stop Cognito from adding the prefix because the form is a hosted web UI.
If this is a hard requirement, the workaround is to create your own login form and use the aws-cognito-sdk
Once you make the call to cognitoUser.authenticateUser in the code below the Pre authentication trigger will fire the Lambda function and you will need to handle the error and parse it to remove the unwanted prefix.
Hope this Helps
aws Examples: Using the JavaScript SDK
var authenticationData = {
Username : 'username',
Password : 'password',
};
var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
var poolData = { UserPoolId : 'us-east-1_TcoKGbf7n',
ClientId : '4pe2usejqcdmhi0a25jp4b5sh3'
};
var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
var userData = {
Username : 'username',
Pool : userPool
};
var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
var accessToken = result.getAccessToken().getJwtToken();
/* Use the idToken for Logins Map when Federating User Pools with identity pools or when passing through an Authorization Header to an API Gateway Authorizer*/
var idToken = result.idToken.jwtToken;
},
//Your message from the Lambda will return here, you will need to parse the err to remove the unwanted prefix*
onFailure: function(err) {
alert(err);
},
});

Downloading an attachment from Teams across a Bot Nodejs

I'm facing the following error when I try to get the attachment in Microsoft Teams with Bot Builder v4:
{"message":"Authorization has been denied for this request."}
everything works fine with the version 3, as far as I know in Teams is necessary a token in order get the binary array of the file.
In the v3 I'm able to get the jwt token in this way:
connector.getAccessToken.bind(connector)
and then I use it in the header of the GET request =>
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/octet-stream'
}
In the v4:
context.adapter.getUserToken(step.context, CONNECTION_SETTING_NAME);
is there another way to get a valid token in the v4?
To get the token just call the prompt again. You can find the auth sample here for node. The sample happens to use a waterfall dialog, which may not be needed in your case
let prompt = await step.prompt(OAUTH_PROMPT);
If the token is valid and not expired you can get the token like below, if the token is not valid or the user does not have a token they will be prompted to log in. Otherwise the token will be in the prompt result.
var tokenResponse = prompt.result;
if (tokenResponse != null) {
await step.context.sendActivity(`Here is your token: ${ tokenResponse.token }`);
}
These comments from the sample should help explain
// Call the prompt again because we need the token. The reasons for this are:
// 1. If the user is already logged in we do not need to store the token locally in the bot and worry
// about refreshing it. We can always just call the prompt again to get the token.
// 2. We never know how long it will take a user to respond. By the time the
// user responds the token may have expired. The user would then be prompted to login again.
//
// There is no reason to store the token locally in the bot because we can always just call
// the OAuth prompt to get the token or get a new token if needed.
these are the steps
/**
* WaterfallDialogStep to process the user's picture.
* #param {WaterfallStepContext} step WaterfallStepContext
*/
async processPhotoStep(step) {
await this.writeLogInTheStorage('Start downloading picture....');
await this.handleIncomingAttachment(step);
return await step.endDialog();
};
/**
* responds to the user with information about the saved attachment or an error.
* #param {Object} turnContext
*/
async handleIncomingAttachment(step) {
// Prepare Promises to download each attachment and then execute each Promise.
const attachment = step.context.activity.attachments[0];
const tokenIsRequired = await this.checkRequiresToken(step.context);
const dc = await this.dialogs.createContext(step.context);
const token = await dc.beginDialog(LOGIN_PROMPT); //await step.context.adapter.getUserToken(step.context, CONNECTION_SETTING_NAME);
let file = undefined;
if (tokenIsRequired) {
file = await this.downloadAttachment(token.result.token, attachment.contentUrl);
}
else {
file = await requestX(attachment.contentUrl);
}
await OAuthHelpers.postPhoto(step.context, token.result, file);
}
async downloadAttachment(token, url) {
const p = new Promise((resolve, reject) => {
request({
url: url,
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/octet-stream'
}
}, async function (err, response, body) {
const result = body
if (err) {
console.log(err);
//await this.writeLogInTheStorage('err 1 : ' + err);
reject(err);
} else if (result.error) {
console.log(result.error);
//await this.writeLogInTheStorage('err 2 : ' + err);
reject(result.error.message);
} else {
// The value of the body will be an array.
console.log(result);
//await this.writeLogInTheStorage('success : ' + result);
resolve(result);
}
});
});
return p;
}

Google Sign-in User Changed Listener

When user lands on page, the Google auth is loaded and initiated. The Google Sign-in API provides a listener function that's triggered if the user changes (GoogleAuth.currentUser.listen()). The expected behavior for the feature that I want to implement involves watching for users logging into gmails within other tabs in the browser.
const userChanged = user => {
if (user) {
console.log('CHANGED TO USER: ', user);
}
};
const loadGoogleAuth = () => {
window.gapi.load('auth2', () => {
const gapiAuth = window.gapi.auth2;
gapiAuth.init({
client_id: '<YOUR-CLIENT-ID>.apps.googleusercontent.com',
cookie_policy: 'none', //my hunch is this could be affecting listener capabilities?
scope: 'profile email openid',
});
const authInstance = gapiAuth.getAuthInstance();
const element = document.getElementById('googleSignIn');
authInstance.attachClickHandler(
element,
{},
googleUser => {
console.log(`Signed in: ${googleUser.getBasicProfile().getName()}`);
},
error => {
console.log('Sign-in error', error);
}
);
authInstance.currentUser.listen(userChanged);
});
};
const withLifeCycles = lifecycle({
componentDidMount() {
if (window.gapi) {
loadGoogleAuth();
}
},
});
This is working, but only for the first gmail that the user signs in with or the gmail that is already signed in before the user lands on the page. In other words, if I log in with Gmail A, it detects the user change but it won't detect Gmail B if I log out of Gmail A and log into Gmail B afterwards. However, it will detect relogins for Gmail A subsequently and not for any other gmail addresses. Is it possible for the listener function to detect sign-ins on any gmail address or only for the firs gmail that is detected?
You need to listen to signed-in changes too. That way you will detect that user signed out from account A and signed in to B.
auth2.isSignedIn.listen(signinChanged)
see https://developers.google.com/identity/sign-in/web/listeners

Error: Client is unauthorized to retrieve access tokens (ServiceAccountCredentials) nodejs

I have been trying to create an app to list all gmail labels of given user by using service account.
Service account have domain-wide delegation and it's raise error when I ran this script "Client is unauthorized to retrieve access tokens".
var {google} = require('googleapis');
const SCOPES = [
'https://www.googleapis.com/auth/gmail.readonly'
];
var emailToLoginWith = 'useremail#anydomain.com';
var key = require('json_key_file_name.json');
var jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
SCOPES,
emailToLoginWith
);
jwtClient.authorize( function (err, tokens) {
if (err) {
console.log(err);
return;
}
console.log('tokens : ', tokens);
listLabels(jwtClient);
});
function listLabels(auth) {
var gmail = google.gmail({version: 'v1', auth: auth });
gmail.users.labels.list({
userId: 'user_id',
}, function(err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return;
}
console.log('labels response', response);
var labels = response.labels;
if (labels.length == 0) {
console.log('No labels found.');
} else {
console.log('Labels:');
for (var i = 0; i < labels.length; i++) {
var label = labels[i];
console.log('- %s', label.name);
}
}
});
}
Client is unauthorized to retrieve access tokens.
This error normally happens when you have created the incorrect type of credentials on Google Developer console. You say that you are using a service account make sure that you have download the correct json file from. After that credentials and the code used to use them are diffrent. You cant use the credentials for a service account with code from browser you will get this error.
I am not a node developer but i found this
http://isd-soft.com/tech_blog/accessing-google-apis-using-service-account-node-js/
You may want to try to get the service account working with drive first. Then when that works move to gmail domain delegation with gmail and service accounts can be tricky sometimes its better to have an example you know works to build off of.

Resources