How to check if Google Client_ID and Client_Secret Valid or not - node.js

I am making a module in Node.js where the user can make their own Livestream Bot for youtube. The module requires the user to provide their client id and client secret of their application. But how do I check if the client id and client secret they entered is valid and if it isn't throw an appropriate error. Is there an endpoint to do that?

There is no way of validating the client id or the client secret. The only way is to try them and if they dont work then display an error to the user.
You could maybe come up with a regex test for the client_id but i wouldn't recommend it as the format has changed several times over the years which would cause problems in your application.
I would just say try to use it if it fails give them an error message.
Note: I hope you are not prompting people to give you their client id and client secret as its against googles TOS for people to shire their client credentials if you are enc urging people to give you their credentials you are telling them to break the TOS.

Valid Code:
function ProjectValid($PostArray){
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://oauth2.googleapis.com/token',
CURLOPT_POST => true,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Content-Type: application/x-www-form-urlencoded',
],
CURLOPT_POSTFIELDS => http_build_query([
'code' => md5(time()),
'client_id' => $PostArray['ClientID'],
'client_secret' => $PostArray['ClientSecret'],
'redirect_uri' => $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER["HTTP_HOST"].'/'.$URL,
'grant_type' => 'authorization_code'
]),
));
$Response = curl_exec($curl);
curl_close($curl);
if($Array=json_decode($Response,true) and isset($Array['error']) and $Array['error'] == 'invalid_grant'){
return true;
}else{
return false;
}
}

Related

PHPMailer with Gmail API auth works with unlimited scope, does not work with limited scope

I am trying to set up a application that will use PHPMailer with Gmail API functionality.
I have written my program and tested it and it worked great. The last step for me was to see if I could make the system a bit more secure and follow best practices by only requesting the scope that is needed.
This version of the code works.
$params = [
'clientId' => $clientKeys['clientID'],
'clientSecret' => $clientKeys['secretKey'],
'redirectUri' => $oauth2redirectURI,
'accessType' => 'offline'
];
$provider = new Google($params);
$options = [
'scope' => [
'https://mail.google.com/'
]
];
However, when I reduce the scope to this:
$params = [
'clientId' => $clientKeys['clientID'],
'clientSecret' => $clientKeys['secretKey'],
'redirectUri' => $oauth2redirectURI,
'accessType' => 'offline'
];
$provider = new Google($params);
$options = [
'scope' => [
'https://www.googleapis.com/auth/gmail.send'
]
];
The initial authorization works to get the refresh token and such.
However, PHPMailer no longer is able to send any messages as that user. Instead when attempting to send email messages, PHPMailer throws the following error:
SMTP connect() failed. https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting1

Firebase custom claims doesn't seem to work

I have an issue with my project - Node.js and Firebase.
I want to add an admin role so I use custom claims according to the docs.
I have 3 functions in the cloud functions:
exports.addAdminRole = functions.https.onCall((data, context) => {
return admin.auth().setCustomUserClaims(context.auth.uid, {
admin: true
}).then(() =>'Success!')
.catch(err => err)
})
exports.getUserRecord = functions.https.onCall((data, context) => {
return admin.auth().getUser(context.auth.uid)
.then(userRecord => userRecord.customClaims)
.catch(err => err)
})
exports.deleteAdmin = functions.https.onCall((data, context) => {
return admin.auth().setCustomUserClaims(context.auth.uid, null)
.then(() => 'Deleted admin!')
.catch(err => err)
})
I call the functions directly in the client (http callable) and addAdminRole returns 'Success!' and seems to work. getUserRecord seems to work as well and returns {admin: true} as my custom claims.
Now for the problem. I defined a function to get the user claims in the client side like mentioned in the docs:
getRecords() {
firebaseInstance.auth.currentUser.getIdTokenResult()
.then(idTokenResult => {console.log("ADMIN:", idTokenResult)})
.catch(err => console.error(err))
}
(firebaseInstance is imported and works fine in the whole project)
I don't understand why but the code in the client side returns an object with the claims property but this property doesn't have the admin claim I added in the cloud functions.
if I try to access idTokenResult.claims.admin like in the docs it logs me UNDEFINED.
Link to the docs - https://firebase.google.com/docs/auth/admin/custom-claims#node.js
Could you clarify to me what's wrong?
Thank you for your help!
The claims of a user are stored in their ID token, which is auto-refreshed by the SDK every hour.
Setting a custom claim to a user's profile from the Admin SDK does not force an auto-refresh of the ID token of the clients, so it may take up to an hour before the claim shows up in the clients. The documentation hints at this with:
Once the latest claims have propagated to a user's ID token, you can get them by retrieving the ID token: ...
This documentation could be more explicit though, so I'd recommend leaving feedback with the button at the bottom of that page.
To ensure a claim propagates to a client sooner, you should force the client to refresh its ID token (for example, by passing true to getIdTokenResult().

How do I retrieve Stripe Session ID for a connected account?

I'm using Stripe checkout. I create the session and process the charge. On success, I have the session id. I want to retrieve the session object for the connected account. (This works fine for a charge to my standard account, but fails when for retrieving the session for the connected account).
For reference, here's the PHP code for creating the session before the charge:
\Stripe\Stripe::setApiKey($Skey);
$session = \Stripe\Checkout\Session::create([
'customer_email' => $Email,
'payment_method_types' => ['card'],
'line_items' => $itms,
'payment_intent_data' => [
'description' => $Web_Short_Name . '*' .$Transaction_ID,
'application_fee_amount' => $Fee,
'metadata' => ['Transaction_ID' => $Transaction_ID],
],
'success_url' => 'https://[myweb]/success.php?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => 'https://[myweb]/cart.php',
],
['stripe_account' => $Stripe_Account] );
}
FIRST ATTEMPT:
$s = $_GET['session_id'];
$stripe = new \Stripe\StripeClient(
['api_key' => '[my secret key'],
['stripe_account' => '[connected account]']
);
$s2=$stripe->checkout->sessions->retrieve($s,[]);
SECOND ATTEMPT:
$s = $_GET['session_id'];
\Stripe\Stripe::setApiKey('[my secret key]');
$stripe = new \Stripe\StripeClient(
'[my secret key]'
);
$s2=$stripe->checkout->sessions->retrieve($s,[]);
Thanks in advance!
Bob
(I've used StackOverflow as a resource for years...but this is my first post).
For connected accounts, you can fill the second parameter of the retrieve function, just like you did when creating the session:
\Stripe\Stripe::setApiKey('<your API key>');
$session = \Stripe\Checkout\Session::retrieve('<the session id>', [
'stripe_account' => '<the id of the connected Stripe account>'
]);
Got what I needed to work. Essentially I was looking for the PaymentIntent object when I got the call to my webhook from Stripe.
Snippet from my checkout webhook:
<?
require __DIR__ . '/vendor/autoload.php';
$payload = #file_get_contents('php://input');
$event = null;
try {
$event = \Stripe\Event::constructFrom(
json_decode($payload, true)
);
} catch(\UnexpectedValueException $e) {
// Invalid payload
http_response_code(400);
exit();
}
// Handle the event
\Stripe\Stripe::setApiKey({YOUR_SECRET_KEY});
$session_id = $event->data->object->id;
switch ($event->type) {
case 'checkout.session.completed':
$checkout = $event->data->object; // contains a \Stripe\PaymentIntent
// Then define and call a method to handle the successful payment intent.
//handleCheckoutSucceeded($checkout);
handleCheckout($db,$Transaction_ID,$session_id,'SUCCEEDED');
?>
\Stripe\Stripe::setApiKey('<your API key>');
$session = \Stripe\Checkout\Session::retrieve('<the session id>',[], [
'stripe_account' => '<the id of the connected Stripe account>'
]);
The second param array should be empty if no other parameters to pass and stripe_account is to be passed in the third array which is for the options array.

firebase-admin: Get token of failed notification delivery result

Let's say we want to send a notification to two registrationTokens (only Android devices, no iOS) like this:
const tokens = ['tokenA', 'tokenB'];
const payload = {badge: 1, title: 'Hello', body: 'world'};
const options = {priority: 'high',contentAvailable: false, timeToLive: 60 * 60 * 24};
const admin = FirebaseAdmin.initializeApp({/*config here...*/});
admin.messaging().sendToDevice(deviceTokens, payload, options)
.then((response) => {
response.results.forEach((deviceResult) => {
if (deviceResult.error) {
console.log('Delivery failed. Showing result:\n', deviceResult);
}
});
});
The user who's device once registered with tokenB deleted the app from his device. Therefore the token is not registered anymore with firebase.
The error object looks like this then:
Delivery failed. Showing result:
{"error":
{
"code":"messaging/registration-token-not-registered",
"message":"The provided registration token is not registered. A previously valid registration token can be unregistered for a variety of reasons. See the error documentation for more details. Remove this registration token and stop using it to send messages."
}
}
The Problem:
My problem is, that I only know that one of the deliveries failed. But I don't know to which token the error is related. Therefore I cannot remove the outdated token from the database.
The Question:
Is there a way to find out for which tokens the deliveries have failed?
Github Issue Link: https://github.com/firebase/firebase-admin-node/issues/600
You need to use the index in forEach and get the token from your array you passed in sendToDevice.
Official docs: https://firebase.google.com/docs/reference/admin/node/admin.messaging.MessagingDevicesResponse
This seems like a hack but it works for me when I have multiple device tokens of a single user as I have to store new one whenever they login.
const tokens = ['tokenA', 'tokenB'];
const payload = {badge: 1, title: 'Hello', body: 'world'};
const options = {priority: 'high',contentAvailable: false, timeToLive: 60 * 60 * 24};
const admin = FirebaseAdmin.initializeApp({/*config here...*/});
admin.messaging().sendToDevice(deviceTokens, payload, options)
.then((response) => {
response.results.forEach((deviceResult,index) => {
if (deviceResult.error) {
let failedToken = tokens[index];
// Now use this token to delete it from your DB, or mark it failed according to your requirements.
}
});
});
This method is also used in firbease samples as well: https://github.com/firebase/functions-samples/blob/master/fcm-notifications/functions/index.js

Creating a ACH charge with stripe + plaid - no such token; a similar object exists in live mode

I've had this problem before when trying to make a charge on a live card/customer when in dev mode. I've never made an ACH charge with stripe before and I'm in dev mode for sure.
https://stripe.com/docs/ach#integrating-plaid
js
var linkHandler = Plaid.create({
env: 'tartan',
clientName: 'Stripe / Plaid Test',
key: '[Plaid key]',
product: 'auth',
selectAccount: true,
onSuccess: function(public_token, metadata) {
// Token & Account ID - I use this for subsequent cURL requuest
console.log('public_token: ' + public_token);
console.log('account ID: ' + metadata.account_id);
},
});
// Trigger the Link UI
document.getElementById('linkButton').onclick = function() {
linkHandler.open();
};
Response is valid. I use the public_token and account ID from above:
$data = array(
'client_id' => 'MY_CLIENT_ID',
'secret' => 'MY_SECRET',
'public_token' => 'MY_PUBLIC_TOKEN_FROM_ABOVE',
'account_id' => 'MY_ACCOUNT_ID_FROM_ABOVE'
);
$string = http_build_query($data);
//initialize session
$ch=curl_init("https://tartan.plaid.com/exchange_token");
//set options
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//execute session
$keys = curl_exec($ch);
$keys = json_decode($keys);
//close session
curl_close($ch);
This also results in a valid response object:
{
access_token: 'MY_ACCESS_TOKEN',
account_id: 'MY_ACCOUNT_ID',
stripe_bank_account_token: 'MY_STRIPE_BANK_ACCOUNT'
}
This is where I'm mixed up I suppose. The docs say:
The response will contain a verified Stripe bank account token ID. You can attach this token to a Stripe Customer object, or create a charge directly on it.
However when I create a charge on the bank account token like this:
\Stripe\Stripe::setApiKey("sk_test_MY_TEST_KEY");
$charge = \Stripe\Charge::create(array(
"amount" => 2100,
"currency" => "usd",
"source" => $keys->stripe_bank_account_token, //(btok_MY_TOKEN_FROM_ABOVE)
"description" => "my description"
));
var_dump( $charge );
Error I get is: Fatal error: Uncaught exception 'Stripe\Error\InvalidRequest' with message 'No such token: btoken_MY_BANK_TOKEN; a similar object exists in live mode, but a test mode key was used to make this request.'
That means you created a live bank account token.
If you want to test your integration, you need to generate the Plaid token with the following credentials:
Username: test_plaid
Password: test_good
Code: 1234
This will return a test bank account token that you can use in an API request sent with your Stripe test secret API key (sk_test_...).
If you want to process a live charge, then you need to use real credentials in Plaid link to get a real bank account token back, then use the bank account token in an API request sent with your Stripe live secret API key (sk_live_...).

Resources