If I try to call the GMail API, I get the following error in return:
{
"error": {
"errors": [{
"domain": "global",
"reason": "failedPrecondition",
"message": "Bad Request"
}],
"code": 400,
"message": "Bad Request"
}
}
First I generate a token
var sHead = JSON.stringify({
"alg": "RS256",
"typ": "JWT"
});
var iat = timeStampf();
var exp = iat + 3600;
var sPayload = JSON.stringify({
"iss": client_email,
"sub": client_email,
"scope": "https://mail.google.com/",
"aud": "https://www.googleapis.com/oauth2/v3/token",
"exp": exp,
"iat": iat
});
var sJWS = KJUR.jws.JWS.sign("RS256", sHead, sPayload, private_key);
var paramstoken = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" + sJWS
getToken("POST", "/oauth2/v3/token", paramstoken, jsonData, replier);
/*rest petition
return 200 ok {
"access_token": "1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M",
"token_type": "Bearer",
"expires_in": 3600
}
*/
Next I test the token
function testToken(accessToken, replier) {
var client = vertx.createHttpClient().host(urlbase).port(443).ssl(true).maxPoolSize(10);
var request = client.request("GET", "/gmail/v1/users/me/messages", function(resp) {
console.log('server returned status code: ' + resp.statusCode());
console.log('server returned status message: ' + resp.statusMessage());
resp.bodyHandler(function(body) {
replier(JSON.parse(body.toString()));
});
});
request.headers()
.set("Content-type", contentType)
.set("Authorization", "Bearer " + accessToken);
request.end();
client.close();
}
And I get an error 400 bad request in return. But if I use a different scope, for example Google+, I get a 200 ok.
I think the error is '"sub":client_email'. I tried add more GMails in the Google console, and with 3 mails auto create for the project, only the mail from Json return me failed precondition if try with others mails including auto created for project and my own GMail(the GMail owner of project) error is 401 unautorized_client.
Or do I need other "grant_type Use the following string, URL-encoded as necessary: urn:ietf:params:oauth:grant-type:jwt-bearer"?
de13#appspot.gserviceaccount.com
7686#developer.gserviceaccount.com///this is de mail downloaded in json from credentials
768-compute#developer.gserviceaccount.com
(this is server side js)
The problem seems with your OAauth credentials. On Google Developer Console -> Credentials, when you try to get new credentials, there are 3 options, 1) Web Application (This is the one you should be using) 2) Service Account (Which is I believe you are trying to use right now), 3) Installed Application (For iOS & Android apps)
Also while authenticating, mention that you will be trying to access the service content in 'offline' mode, which will give you a 'refresh Token' to make subsquent requests without user's involvment.
You are able to access Google+ because its public data as compared to Gmail where you are trying to access messages for a certain user (Private data). Service account credentials will not give you access to a user's private data and you must impersonate the user whose emails you are trying to access via authentication using Web Application credentials in your case.
Hope this helps....
"sub": client_email
seems to be incorrect.
You should use your gmail address in the sub field, as you are trying to impersonate this email address.
Related
I have the following code for TESTING PURPOSES:
$.ajax({
url: 'https://fcm.googleapis.com/v1/projects/[PROJECT]/messages:send',
type: 'POST',
headers:{
"Authorization":"Bearer "+[Access Token from FireBase Auth]
},
contentType:"application/json",
data: {
"message":{
"token": [TOKEN from messaging.getToken],
"notification" : {
"body" : "This is an FCM notification message!",
"title" : "FCM Message",
}
}
},
success: function () { },
error: function () { },
});
This always results in the following response with a 401()...
{
"error": {
"code": 401,
"message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED"
}
}
What am I doing wrong?
In the docs we linked in comments: https://firebase.google.com/docs/cloud-messaging/js/first-message
Under Retrieve Registration Token, you see this code:
messaging.getToken().then(function(currentToken) {
if (currentToken) {
sendTokenToServer(currentToken);
updateUIForPushEnabled(currentToken);
} else {
// Show permission request.
console.log('No Instance ID token available. Request permission to generate one.');
// Show permission UI.
updateUIForPushPermissionRequired();
setTokenSentToServer(false);
}
}).catch(function(err) {
console.log('An error occurred while retrieving token. ', err);
showToken('Error retrieving Instance ID token. ', err);
setTokenSentToServer(false);
});
You'll notice the sendTokenToServer() function, that's not their function, that's supposed to be yours. You call their getToken() and in the promise you take the result and send it up, would look like this:
function sendTokenToServer(currentToken) {
$.post({
url: 'yourServer.com/some_token_receiving_endpoint',
type: 'post',
data: {token: currentToken}
});
}
Then on the server, you'd receive that, and store it, likely in a database, related to their profile information.
Then, either at that moment, or, at a later time, you can query your database for those you want to notify, grab that token, and in conjunction with your access token stored securely on your server, you can then send the notification from there.
Typically, NodeJS, PHP, Python, or Ruby. As events happen, or on a schedule, your server can send notifications like this:
<?php
// Get some http client service for your language
$client = new GuzzleHttp\Client();
// Get your user or users (with their tokens that you've stored)
$user = Db.someQueryReturningUser();
// Your message
$jsonData = '{
"message":{
"token": [TOKEN from messaging.getToken],
"notification" : {
"body" : "This is an FCM notification message!",
"title" : "FCM Message",
}
}
}';
// Send Mesage
$client->post('https://fcm.googleapis.com/v1/projects/[PROJECT]/messages:send',
[
'headers' => [
'Authorization' => 'Bearer ' . [Access Token from FireBase Auth]
],
'json' => $jsonData
]);
In a very broad sense, what you're doing wrong is trying to call the FCM APIs from a web browser client. FCM messages are intended to be sent from a backend server under your total control. The authorization token that you need to send is going to effectively have admin privileges to send messages to any and all of your users, and you don't want to give that up to clients, as it's massive security issue.
From the documentation:
Requests sent to FCM from your app server or trusted environment must
be authorized. The FCM HTTP v1 API uses a short-lived OAuth 2.0 access
token generated for a service account associated with your Firebase
project. The legacy protocols use long-lived API keys retrieved from
the Firebase console. In both cases, you must add the required
credential to each message request sent to FCM.
In other words, you're not supposed to give clients access to send messages with your privileged service account credentials. The rest of that page of documentation describes how to actually do the world of authorizing the send request.
I'm trying few days to composing mail sending with node.js to gmail with no success :-(
at the beginning of tries I try to send form submittion to my gmail but first I need to understand how do I sent simple mail from body to gmail,
perhaps I wrong with syntax or missing something?
actually I uses "heroku" as storage and domain for my site
I tried several plugins (such as mailgun, send grid and more) but the process was too complicated integrate their API's into my site.
actually,
I find this article in stack overflow that describe how to send the via nodemailer in relation to my error - URL: node.js nodemailer gmail error
when i copy the answer1 to my code i still receiving error.
I' also pass trough all gmail setup how to turn off security block for less secured apps but i'm still receiving error.
all the requires installed over my npm
npm install --save
** code **
var nodemailer = require('nodemailer');
var smtpTransport = require('nodemailer-smtp-transport');
function handleSayHello(req, res) {
var transport = nodemailer.createTransport(smtpTransport({
service: 'gmail',
auth: {
user: 'myGmail#gmail.com', // my mail
pass: 'myPassword'
}
}));
var mailOptions = {
from: 'myGmail#gmail.com', // sender address
to: 'myGmail#gmail.com', // list of receivers
subject: 'Email Example' // Subject line
//text: text //, // plaintext body
//html: '<b>Hello world ?</b>' // You can choose to send an HTML body instead
};
transport.sendMail(mailOptions, function (error, info) {
if (error) {
console.log(error);
res.json({
yo: 'error',
err: error
});
} else {
console.log('Message sent: ' + info.response);
res.json({
yo: info.response,
info: info
});
};
});
}
router.get('/', function (req, res, next) {
handleSayHello(req, res);
});
then i receive this error:
{
"yo": "error",
"err": {
"code": "EAUTH",
"response": "534-5.7.14 <https://accounts.google.com/signin/continue?sarp=1&scc=1&plt=AKgnsbtx\n534-5.7.14 e8jnXXDaDg-dpFfc3H5ljeaBdmnxbXBDXMh-aXS-mmV4gQsXMSZLiiJHDXuOr3wy-IR2Zp\n534-5.7.14 xnznWLf5y5e3xTbJNlZBNcxBMajM9-SFQGy1MQM2XZRpHgtywuDtEj5iPiP0b2T6Wjbsxg\n534-5.7.14 hgehfzufG6h13qhjQK5IgRPNQsSICRQBtRCl3E1J62wFo8bnvZv4peY5aK55JMpwhSavJb\n534-5.7.14 ho-b9ExGGsXFmw_Er6lc8m3vCmO_Q> Please log in via your web browser and\n534-5.7.14 then try again.\n534-5.7.14 Learn more at\n534 5.7.14 https://support.google.com/mail/answer/78754 t35sm887733qtc.40 - gsmtp",
"responseCode": 534,
"command": "AUTH PLAIN"
}
}
if someone can take my code and fix it (if needed) and explain simply what do i need to modify in my google account I'll be great-full so much!
to allow from google/gmail, you should follow these links and allow to less secure apps access:
unlock account
and
Less secure apps
Let me know if still have issue, I'll check/debug your code.
I'm trying to send a basic notification using Firebase Cloud Messaging, but I cannot for the life of me figure out how to include any actual content in the notification.
I'm starting with essentially a stripped-down version of what can be found here. I have three files, a manifest.json, which looks like this:
{ "gcm_sender_id": "my sender id" }
an index.html, which looks like this:
<html>
<head><link rel="manifest" href="manifest.json"></head>
<body>
<div id="endpoint-show">There doesn't seem to be an endpoint right now</div>
<script src="https://www.gstatic.com/firebasejs/3.5.0/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/3.5.0/firebase-messaging.js"></script>
<script>
var config = {apiKey: "my key", authDomain: "my domain", messagingSenderId: "my id"};
firebase.initializeApp(config);
const messaging = firebase.messaging();
messaging.requestPermission()
.then(function() { console.log('Notification permission granted.'); })
.catch(function(err) { console.log('Unable to get permission to notify. ', err); });
navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
serviceWorkerRegistration.pushManager.subscribe({userVisibleOnly: true})
.then(function(subscription) {
document.getElementById("endpoint-show").innerHTML = "<p>" + subscription.endpoint.split('/\').slice(-1)[0] + "</p>";
})
});
navigator.serviceWorker.register('./service-worker.js')
</script>
</body>
<html>
and a service-worker.js, which looks like:
self.addEventListener('push', function(event) {
console.log('Received a push message', event);
event.waitUntil(self.registration.showNotification("title", { body: "body" }));
});
This seems to be about the minimum amount of code required to register a subscription for push notifications and establish a service worker to handle them.
I then send notifications using a curl command like the one shown here. Ie, POST to https://fcm.googleapis.com/fcm/send with those certain headers set and a body that contains a "too" field which is equal to the one that my idex.html shows. This works, insofar that I get a notification on my computer with the title "title" and the body "body".
But here is where I am stuck: It seems like the only data I can send through this request is the fact that a notification happens, and not any other (actual) data. The first example I linked just hard-codes a notification, as does my own code, but I would like to be able to send a request with arbitrary data. The documentation here seems to indicate that I should be able to set either the data or notification field of the request, and get a notification that has that data, but that doesn't seem to work. I have set both fields in the request I'm making, so the whole thing looks something like this (which I am sending using postman):
POST /fcm/send HTTP/1.1
Host: fcm.googleapis.com
Content-Type: application/json
Authorization: key=my key
Cache-Control: no-cache
Postman-Token: some token
{
"notification": {
"body": "notification body",
"title": "notification title"
},
"data": {
"body": "data body",
"title": "data title"
},
"to" : "my endpoint"
}
But I can't figure out how to actually retrieve any of that data. The event that my service worker captures does not contain any of this data. None of the documentation I have looked at seems to describe how to actually get this data through the web, only on Android or iOS, but obviously it is possible since many website implement dynamic notifications.
I suspect that I have some fundamental misunderstanding of how all of this works, which would not be surprising since I have very little experience with any kind of web development. If what I'm trying to do is impossible, or much be done a completely different way, let me know.
And just to reiterate, here is exactly what I am trying to do:
Send a request (whether that is to a webserver that I write or to firebase).
Have a notifcation pop up on my Chrome with information from that request.
You seem to be mixing the new firebase messaging lib with the old style service worker code.
After you get the permission, you need to call the getToken API.
// service.js
// after request permission is successful
// Get Instance ID token. Initially this makes a network call, once retrieved
// subsequent calls to getToken will return from cache.
messaging.getToken()
.then(function(currentToken) {
if (currentToken) {
sendTokenToServer(currentToken);
updateUIForPushEnabled(currentToken);
} else {
// Show permission request.
console.log('No Instance ID token available. Request permission to generate one.');
// Show permission UI.
updateUIForPushPermissionRequired();
setTokenSentToServer(false);
}
})
.catch(function(err) {
console.log('An error occurred while retrieving token. ', err);
showToken('Error retrieving Instance ID token. ', err);
setTokenSentToServer(false);
});
Its debatable, but you also need to create a service work with filename as firebase-messaging-sw.js. Reference can be found here.
And in this service worker you need to put something like this:
// firebase-messaging-sw.js
'use strict';
console.log('Starting service worker');
if( 'function' === typeof importScripts) {
importScripts('https://www.gstatic.com/firebasejs/3.5.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.5.0/firebase-messaging.js');
importScripts('core/decoder.js');
// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
'messagingSenderId': 'YOUR-SENDER-ID'
});
// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
var shinyData = payload || {};
console.log('[firebase-messaging-sw.js] Received background message ', payload, shinyData);
return self.registration.showNotification(shinyData.title, {
body: shinyData.body,
icon: shinyData.icon,
data: {url: shinyData.tag}
})
});
}
You may find this gist I created helpful.
I have spent couple of days implementing my own mail server using node.js. I used modules like "smtp-server" for creating smtp server and also "smtp-connection" to connect and send mail to it. But I'm getting confused because I don't know how to send mails from my smtp server to providers smtp servers like google or yahoo.
Can anyone help me?
Here is my code for more information:
My index.js file:
var SMTPServer = require('smtp-server').SMTPServer;
var port = 9025;
var serverOptions = {
name: "smtp-interceptor",
onConnect: onConnect,
onAuth: onAuth,
onData: onData
};
var server = new SMTPServer(serverOptions);
server.listen(port, 'localhost', function () {
console.log('SMTP server is listening on port ' + port);
});
function onConnect(session, callback) {
console.log('Connected');
return callback(); // Accept the connection
}
function onData(stream, session, callback) {
stream.pipe(process.stdout); // print message to console
console.log('Session \n', session.envelope);
stream.on('end', callback);
}
function onAuth(auth, session, callback){
if(auth.username !== 'Mahan' || auth.password !== 'Tafreshi') {
return callback(new Error('Invalid username or password'));
}
callback(null, {user: 123}); // where 123 is the user id or similar property
}
And my connection.js file:
var SMTPConnection = require('smtp-connection');
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var connection = new SMTPConnection({
host: 'localhost',
port: 9025,
secure: false
});
connection.connect(function (){
console.log('Connceted to SMTP server');
var auth = {
user: 'Mahan',
pass: 'Tafreshi'
};
connection.login(auth, function (err) {
if(err)
return console.log('Login Failed \n', err);
console.log('Login Successful');
var envelope = {
from: "testapp#testapplocal.com",
to: "mahantp19#gmail.com"
};
var message = 'Test message1';
connection.send(envelope, message, function (err, info) {
if(err)
return console.log('Error : ' + err);
console.log('Message sent');
console.log('Accepted : ' + info.accepted);
console.log('Rejected : ' + info.rejected);
console.log(info.response);
connection.quit();
console.log('Quit connection');
connection.close();
});
});
});
There are many checks an email must pass before it's accepted by most mail providers. These checks attempt to validate the server sending the message is authorized to send on behalf of the sender.
IE: My server can send an email saying it's from "someone-special#somewhere-important.com"... That doesn't mean I'm "anywhere important" by any means.
While you may have had success sending mail from an SMTP server in the past using another technology such as PHP or an Exchange Server, the rules have changed significantly. Gmail has just began full enforcement this year.
I would assume your current issue has nothing to do with node as much as recent changes by the big providers.
Some of the checks that are needed include:
DKIM Keys (DNS Record)
SPF Record (DNS Record)
DMARK has been setup.
Dedicated IP Address for the server is required.
Your servers IP not being blacklisted.
The content of your email passes their filters.
Attempt to have an email sent from your server appear to be from a visitor or customer.
Among many others.
Any domain you want to "Act as Sender" must have these in place for most of the major providers to accept you message.
Google has a great set of tools and walkthroughs on getting an IP/Domain setup.
Visit the Google MX Record Checker and enter in the domain/subdomain you want to use as sender and it will tell you everything that is failing as well as passing.
Alternative Solutions
I use sendgrid.com. They have A node library that makes sending mail very easy. They also provide me the ability to proxy messages via SMTP. This means you can utilize the standard methods to deliver messages. You will just change out "localhost" with an hostname they provide. However, if this is for a new setup, go for the API.
Whomever is hosting your email should offer the ability for you send messages via SMTP or an API
An endless supply of other providers are out their, most of which allow low volume senders to send for FREE.
Word of warning
I tried for a few years keeping up with all the changes and inevitably, I continued to hit barriers of blocked messages with no ability to know until someone did not get an email. If your sending low volume, you should be able to use third parties without paying paying for it. If you are sending high volume, the cost of their service is cheap compared to the endless issues you will encounter even once you get it initially rolling.
PS. I have no affiliation with any email provider or sender. I pay them too.
I am trying to upload an image to Google+ using Javascript/JQuery; I have an access token which I can use to authenticate POST requests successfully but I get the following response when I attempt to use G+ Media Insert (https://developers.google.com/+/domains/api/media/insert):
{
"error": {
"errors": [
{
"domain": "global",
"reason": "forbidden",
"message": "Forbidden"
}
],
"code": 403,
"message": "Forbidden"
}
}
I haven't configured Domain-wide delegation because I expect the user to sign in to generate the access token (is this perhaps the issue?). I have enabled the Google+ Domains API in the Developer console and the relevant scopes are in place but I can't figure out why I receive a 403 error. The following AJAX request is being used:
var postForm = new FormData();
postForm.append("source",[code which generated a blob]);
postForm.append("displayName", "TestUpload");
$.ajax({
url: "https://www.googleapis.com/upload/plusDomains/v1/people/me/media/cloud",
headers: {"Authorization": "Bearer " + acToken},
type:"POST",
uploadType: "multipart/related",
processData:false,
contentType:false,
cache:false,
data: postForm,
});
Any help would be appreciated, I can provide more information if needed.
PS: I am actually using a userID instead of me in the URL for the moment
First, make sure that the userID belongs to the user that you have authenticated (perhaps through this OAuth flow). Unlike methods such as people.get, this method requires authentication, and cannot be called solely with an ID, unless that ID is the currently authenticated user. This is why we recommend using the special value me to avoid confusion.
Second, you need to be certain that the user that is authenticated is a Google Apps user. For example, if the user is a GMail user, the request will get a 403, since the Google+ Domains API is not allowed for non-Domains accounts.