Nodemailer OAUTH Gmail connection (Error: connect ETIMEDOUT 74.125.140.108:465) - oauth-2.0

Im trying to use OAUTH 2.0 to send emails with Gmail API using Nodemailer
I have Enable the Gmail API and i have clientId, clientSecret, refreshToken and accessToken (refreshtoken and accesstoken were taken from developers.google.com/oauthplayground) because these all were needed for OAUTH to work.
i have followed every detail form (https://nodemailer.com/smtp/oauth2/#examples)
But i'm getting following error
{ Error: connect ETIMEDOUT 74.125.140.108:465
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1097:14)
errno: 'ETIMEDOUT',
code: 'ESOCKET',
syscall: 'connect',
address: '74.125.140.108',
port: 465,
command: 'CONN' }
But there is a catch when i use Plain username and password there is no connection timeout error because i have already enabled less secure app access form (https://myaccount.google.com/lesssecureapps) so there is no error in sending emails using plain username and password but this is not a good approach so OAUTH is the solution
here is my code for you to check (its an express app) running in development running on http://localhost:3000
var nodemailer = require('nodemailer');
var transporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
service: 'gmail',
auth: {
type: 'OAuth2',
user: 'mygmailaddress#gmail.com',
clientId: 'clientID',
clientSecret: 'clientSecret',
refreshToken: 'refresh token obtained from https://developers.google.com/oauthplayground',
accessToken: 'access token also obtained from https://developers.google.com/oauthplayground'
}
});
var mailOptions = {
from: 'me <myemailaddress#gmail.com>',
to: '',
subject: 'Subject ',
text: 'Some thing',
};
transporter.sendMail(mailOptions, (error, response) => {
if(error){
console.log(error); // Always giving Error: connect ETIMEDOUT 74.125.140.108:465
} else {
console.log(response);
}
});
it not even try to login because when login fails it gives error of wrong credentials (checked with plain username and password)
*Note please don't mark this question as duplicate because i have check almost all other question and answers but nothing worked
some people said its because of firewall setting i have check that too but didn't worked
So whats the issue and how to solve it

Your port no is wrong for gmail. Use This Format for gmail
const nodeMailer = require('nodemailer');
let transporter = nodeMailer.createTransport({
host: "smtp.gmail.com",
port: 587,
secure: false, // true for 587, false for other ports
requireTLS: true,
auth: {
user: your email,
pass: your password,
},
});
let mailOptions = {
from: 'from#gmail.com',
to: 'to#gmail.com',
subject: 'Sending Email using Node.js',
text: 'That was easy!'
};
transporter.sendMail(mailOptions, function(error, info){
if (error) {
console.log(error);
} else {
console.log('Email sent: ' + info.response);
}
});

After looking to your problem i assume that when generated the OAuth credentials e.g(clienId and Clientsecret ) you did not added https://mail.google.com/ in you scope on connect screen because you would need verification form google to add these scope for the credentials
you must have added this to your scope on (https://developers.google.com/oauthplayground) but i'm sure you have add this to your scope on consent screen as well before generating credentials
OAuth consent screen
as you are in Development mode and running your application on localhost so you don't have private domain and privacy policy
Adding https://mail.google.com to your scope before generating clientId and Cliensecret will resolve your problem

Related

ClientConfigurationError Microsoft OAuth Flow

I am implementing the Microsoft Auth code flow but I am stuck with this error.
Based on this code example, here is how I am initializing the client:
const config = {
auth: {
clientId: process.env.MICROSOFT_CLIENT_ID,
authority: process.env.MICROSOFT_AUTHORITY,
clientSecret: process.env.MICROSOFT_CLIENT_SECRET,
},
};
const cca = new msal.ConfidentialClientApplication(config);
And later I want to create an authentication URL to redirect the user to:
const authCodeUrlParameters = {
scopes: ["user.read"],
redirectUri: "http://localhost:8080/oauth/microsoft",
state: 'state_here',
};
cca
.getAuthCodeUrl(authCodeUrlParameters)
.then((authCodeUrl) => {
return authCodeUrl;
})
.catch((error) => console.log(JSON.stringify(error)));
But I am getting this error: {"errorCode":"empty_url_error","errorMessage":"URL was empty or null.","subError":"","name":"ClientConfigurationError"}
Based on the docs about errors, it looks like it's thrown before requests are made when the given user config parameters are malformed or missing.
Anybody can spot where the configs are malformed?
The error is because of the missing configuration requirements in the application.
And most importantly , check the authorization request url for missing parameters like state and nonce and the redirect url.
Here request URL may require state and nonce parameters form cache as part of authCodeUrlParameters to construct the URL.
In authCodeUrlParameters see which of them is missed as they may lead to url to null.
You try to give your domain in knownAuthority
Ex:
auth: {
clientId: 'xxxx-xx-xx-xx-xxxxx',
authority: '<give authority>',
knownAuthorities: ['<domain here>']
redirectUri: 'https://localhost:8080'
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: false,
secureCookies: false
},
Please make sure the redirect url is in correct format:
See Redirect URI (reply URL) restrictions - Microsoft Entra | Microsoft Learn
After setting the correct url, I could get proper response

MSAL - Network request failed. Please check network trace to determine root cause

I am trying to use MSAL for node.js for OAuth 2.0 authentication, and I'm getting an error when calling acquireTokenByAuthCode.
Network request failed. Please check network trace to determine root cause. | Fetch client threw: Error: HTTP status code 400 | Attempted to reach: https://login.microsoftonline.com/{myTenantId}/oauth2/v2.0/token
Client instance:
const msal = new ConfidentialClientApplication({
auth: {
clientId: process.env.MS_CLIENT_ID,
clientSecret: process.env.MS_SECRET,
authority: process.env.MS_AUTHORITY
}
})
Login route:
const uri = await msal.getAuthCodeUrl({
responseMode: 'query',
redirectUri: `${process.env.APP_URI}/auth/code`,
scopes: ['user.read']
});
res.redirect(uri);
Token route (/auth/code):
try {
const {
accessToken,
} = await msal.acquireTokenByCode({
scopes: ['user.read'],
code: req.query.code,
redirectUri: `${process.env.APP_URI}/auth/code`,
});
res.cookie('token', accessToken);
res.redirect('/');
}
catch(e) {
res.status(401).send(e);
}
I can retrieve an auth code just fine, but I get the error when trying to get the token in the last snippet.
Make sure your clientSecret is correct. I just had this same issue and realized I was using the ID of the secret as opposed to the actual secret itself.
I fixed it. The Application was just missing the openid permission.

react native app auth plugin network error

I tried to create a react native app with OAuth login in my rails app.
I have this react native setup
const config = {
issuer: 'http://app.domain.tld',
clientId: '85bb84b9cb0528b1f64b7c77586507b3ca5e69b11abe36ae1e54e88a6150c21e',
clientSecret: '0d3c0713437e0028a121a0c2294cc9a72f4eb5609416935a2860e20f176c7855',
redirectUrl: 'com.domain://com.domain',
responseType: 'code',
scopes: [],
dangerouslyAllowInsecureHttpRequests: __DEV__,
// clientAuthMethod: 'post',
serviceConfiguration: {
authorizationEndpoint: 'http://app.domain.tld/oauth/authorize',
tokenEndpoint: 'http://app.domain.tld/oauth/token',
// revocationEndpoint: 'http://app.domain.tld/oauth/revoke'
},
additionalParameters: {
approval_prompt: 'force'
}
}
// use the client to make the auth request and receive the authState
try {
const result = await authorize(config)
// result includes accessToken, accessTokenExpirationDate and refreshToken
console.log('result', result)
} catch (error) {
console.log('error', error.message)
}
the oaut app looks like this:
When I press the button to login in my app, I get a browser window where I can login in my rails app and got redirected to the authorize page for the oauth app. After I authorize my app, I got redirected to the app with the message "Network error". With that message I can't find out whats wrong, maybe anyone can help me.
In the server logs I can see this:
Redirected to com.domain://com.domain?code=54f3b4c03ea3724522f9a7983e2ea1b9037336076cd52cb875f9654d5d79784a&state=8xmiicVcPKN980ZDZUwBnw
in the debugger-ui i get this error log:
error Error: Network error
at createErrorFromErrorData (ReactNativeART.js:10)
at ActivityIndicator.js:72
at MessageQueue.__invokeCallback (ReactNativeART.js:472)
at blob:http://localhost:8081/1fccf34b-97b0-4c42-81fa-f0e1391a3ad3:2358
at MessageQueue.__guard (ReactNativeART.js:373)
at MessageQueue.invokeCallbackAndReturnFlushedQueue (blob:http://localhost:8081/1fccf34b-97b0-4c42-81fa-f0e1391a3ad3:2357)
at debuggerWorker.js:80
so i think there is something wrong. The app didn't make any further request to the server to obtain the access_token. Whats wrong?
Use this in config:
dangerouslyAllowInsecureHttpRequests: true,

Gmail API - unable to fetch messages from users in domain using google.auth.jwt

I am trying to fetch all emails under our company domain. I am using Node.js client library. In my last approach I used Oauth method as documented in quickstart guide. It was suggested that I should use JWT to authorize, but I am still unable to figure out how to do it properly in node (due to lack of documentation). This is my code:
var google = require('googleapis'),
gmail = google.gmail('v1');
var key = require('./service_key.json');
var jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
['https://mail.google.com/','https://www.googleapis.com/auth/admin.directory.group']
);
jwtClient.authorize(function(err, tokens) {
if (err) {
console.log(err);
return;
}
gmail.users.messages.list({userId: 'me', auth: jwtClient}, (err, response)=>{
if (err) {
console.log(err);
return;
}
console.log(response);
});//messages.list
});//jwtClient.authorize
I get error :
{ Error: Bad Request
at Request._callback (/home/kunok/code/gapi/node_modules/google-auth-library/lib/transporters.js:85:15)
at Request.self.callback (/home/kunok/code/gapi/node_modules/google-auth-library/node_modules/request/request.js:198:22)
at emitTwo (events.js:106:13)
at Request.emit (events.js:191:7)
at Request.<anonymous> (/home/kunok/code/gapi/node_modules/google-auth-library/node_modules/request/request.js:1057:14)
at emitOne (events.js:101:20)
at Request.emit (events.js:188:7)
at IncomingMessage.<anonymous> (/home/kunok/code/gapi/node_modules/google-auth-library/node_modules/request/request.js:1003:12)
at emitNone (events.js:91:20)
at IncomingMessage.emit (events.js:185:7)
code: 400,
errors:
[ { domain: 'global',
reason: 'failedPrecondition',
message: 'Bad Request' } ] }
I am unable to understand the right approach. I want to skip any user interaction and fetch all mails under domain by running CRON job node.js scripts from the server. I have all admin rights. What is wrong in my approach?
Try changing your jwt format to
var jwt = new googleapis.auth.JWT(
SERVICE_ACCOUNT_EMAIL,
SERVICE_ACCOUNT_KEY_FILE,
null,
['https://www.googleapis.com/auth/admin.directory.group'],
account_with_Admin_SDK_access_to_impersonate#example.com
);
Based from Perform Google Apps Domain-Wide Delegation of Authority code sample(python):
"""Build and returns an Admin SDK Directory service object authorized with the service accounts
that act on behalf of the given user.
Args:
user_email: The email of the user. Needs permissions to access the Admin APIs.
Returns:
Admin SDK directory service object.
"""
credentials = ServiceAccountCredentials.from_p12_keyfile(
SERVICE_ACCOUNT_EMAIL,
SERVICE_ACCOUNT_PKCS12_FILE_PATH,
'notasecret',
scopes=['https://www.googleapis.com/auth/admin.directory.user'])
credentials = credentials.create_delegated(user_email)
Hope this helps!

Populating username & appname using parse-server-mandrill-adapter, not working

Im trying to use parse-server-mandrill-adapter for generating a new password for users who has forgot their passwords.
I am using a parse-server and I am using parse unbuilt function requestPasswordReset to generate a mail to the user. I manage to send the email successfully to the user using parse-server-mandrill-adapter, however I cant manage to get any data concerning the user included in this. E.g fields like username, appname do not populate.
Parse server index.js
emailAdapter: {
module: 'parse-server-mandrill-adapter',
options: {
// API key from Mandrill account
apiKey: process.env.MANDRILL_API_KEY || '',
// From email address
fromEmail: 'kontakt#bonsai.se',
// Display name
displayName: 'no-reply#bonsai.se',
// Reply-to email address
replyTo: 'no-reply#bonsai.se',
// Verification email subject
verificationSubject: 'Please verify your e-mail for *|appname|*',
// Verification email body
verificationBody: 'Hi *|username|*,\n\nYou are being asked to confirm the e-mail address *|email|* with *|appname|*\n\nClick here to confirm it:\n*|link|*',
// Password reset email subject
passwordResetSubject: 'Password Reset Request for *|appname|*',
// Password reset email body
passwordResetBody: 'Hi apa *|username|*,\n\nYou requested a password reset for *|appname|*.\n\nClick here to reset it:\n*|link|*'
}
The main.js file, where the requestPasswordReset function is called, with email, options and callback.
var query = new Parse.Query(Parse.User)
query.equalTo("email", request.params.email)
query.first({
useMasterKey: true
}).then(function (user) {
Parse.User.requestPasswordReset(request.params.email, {
options: {
"username": "test",
useMasterKey: true
}
}).then(function (success) {
console.log(success)
response.success("success")
}, function (error) {
console.log(error)
response.error("error")
});
})
The look of the email.
How the email looks
Take a look at : https://github.com/ParsePlatform/parse-server
Email verification and password reset
Also similar question & answer :
Parse open source server reset password error
I use the MailGun adapter which is included into the open-parse server and works like a charm!
I was able to use the parse-server-mandrill-adapter, and successfully reset user passwords with proper replacements to the email/appname/link:
1) Install the parse-server-mandrill-adapter, (add it to the package.json to be able to deploy it onto the remote server).
2) Update your index.js to support the emailAdapter, source link:
var server = ParseServer({
...
// App Name
appName: 'YourAppName',
// Environment where the user can confirm his e-mail address or reset his password (most likely the same as your 'serverURL')
publicServerURL: 'YourPublicServerURL',
emailAdapter: {
module: 'parse-server-mandrill-adapter',
options: {
// API key from Mandrill account
apiKey: 'API-KEY',
// From email address
fromEmail: 'no-reply#yourdomain.com',
// Display name
displayName: 'no-reply#yourdomain.com',
// Reply-to email address
replyTo: 'no-reply#yourdomain.com',
// Verification email subject
verificationSubject: 'Please verify your e-mail for *|appname|*',
// Verification email body
verificationBody: 'Hi *|username|*,\n\nYou are being asked to confirm the e-mail address *|email|* with *|appname|*\n\nClick here to confirm it:\n*|link|*',
// Password reset email subject
passwordResetSubject: 'Password Reset Request for *|appname|*',
// Password reset email body
passwordResetBody: 'Hi *|username|*,\n\nYou requested a password reset for *|appname|*.\n\nClick here to reset it:\n*|link|*'
}
}
...
});
3) The password reset code:
Parse.User.requestPasswordReset(userEmail, {
success: function() {
// success
},
error: function(error) {
// error
}
});
4) Make sure when running that code, that your app is running from the remote server, it failed for me when running on localhost:1337!

Resources