GSuite Error: Can't create new access token for user - OAuth2 Nodemailer - node.js

I'm trying to send emails through gmail from nodejs but I keep running into this error.
Error: Can't create new access token for user
at XOAuth2.generateToken(c: \apps\node\myapp\node_modules\nodemailer\lib\xoauth2\index.js: 184: 33)
at XOAuth2.getToken(c: \apps\node\myapp\node_modules\nodemailer\lib\xoauth2\index.js: 123: 18)
at SMTPConnection._handleXOauth2Token(c: \apps\node\myapp\node_modules\nodemailer\lib\smtp - connection\index.js: 1697: 27)
at SMTPConnection.login(c: \apps\node\myapp\node_modules\nodemailer\lib\smtp - connection\index.js: 540: 22)
at c: \apps\node\myapp\node_modules\nodemailer\lib\smtp - transport\index.js: 374: 32
at SMTPConnection.<anonymous>(c: \apps\node\myapp\node_modules\nodemailer\lib\smtp - connection\index.js: 215: 17)
at Object.onceWrapper(events.js: 420: 28)
at SMTPConnection.emit(events.js: 314: 20)
at SMTPConnection._actionEHLO(c: \apps\node\myapp\node_modules\nodemailer\lib\smtp - connection\index.js: 1313: 14)
at SMTPConnection._processResponse(c: \apps\node\myapp\node_modules\nodemailer\lib\smtp - connection\index.js: 942: 20)
at SMTPConnection._onData(c: \apps\node\myapp\node_modules\nodemailer\lib\smtp - connection\index.js: 749: 14)
at TLSSocket.SMTPConnection._onSocketData(c: \apps\node\myapp\node_modules\nodemailer\lib\smtp - connection\index.js: 195: 44)
at TLSSocket.emit(events.js: 314: 20)
at addChunk(_stream_readable.js: 304: 12)
at readableAddChunk(_stream_readable.js: 280: 9)
at TLSSocket.Readable.push(_stream_readable.js: 219: 10) {
code: 'EAUTH',
command: 'AUTH XOAUTH2'
}
I have set up my G Suite account, created a service account and enabled the Gmail API for that project. I have also enabled G-Suite domain wide delegation for the service account and added the client ID and the proper scope to my G Suite account.
I downloaded my credentials file and renamed it to use .js so I could import it as an ES module.
import nodemailer from "nodemailer";
import * as key from "../credentials.js";
const SENDER_EMAIL = "support#domain.com";
const RECEIVER_EMAIL = "customer#gmail.com";
async function start() {
const transporter = nodemailer.createTransport({
host: "smtp.gmail.com",
port: 465,
secure: true,
auth: {
type: "OAuth2",
user: SENDER_EMAIL,
serviceClient: key.client_id,
privateKey: key.private_key,
},
});
try {
await transporter.verify();
await transporter.sendMail({
from: SENDER_EMAIL,
to: RECEIVER_EMAIL,
subject: "Hello Humans",
text: "The quick brown fox jumps over the lazy dog.",
});
} catch (err) {
console.error(err);
}
}
export default start;
Now, calling start() errors out with the above message. I can't figure it out. Help.

I hope you solved your issue 11 months ago but, for those who find this, I had the same issue and found the answer here. I believe this might be your case specially because you mentioned that hardcoding the value does work.
Apparently, the \n in the private key brings some issues when used this way. In my case, I'm taking the private key value from Firebase runtime config, and following suggestions in that link I ended up with a replace(/\\n/g, '\n') on the key:
const nodemailerTransporter = nodemailer.createTransport({
host: env.nodemailer.host,
port: 465,
secure: true, // true for TLS 465, false for other ports
auth: {
type: "OAuth2",
user: env.nodemailer.user,
serviceClient: env.nodemailer.clientid,
privateKey: env.nodemailer.privatekey.replace(/\\n/g, '\n'), // <=== HERE
accessUrl: env.nodemailer.tokenuri, // <== HERE
},
});
which does work. I'm using "host": "smtp-relay.gmail.com" but it's the same for your smtp.gmail.com case.
Also consider the accessUrl (the token_uri as taken from credentials.json):
"token_uri": "https://oauth2.googleapis.com/token"
which is not the same as the default used by nodemailer (https://accounts.google.com/o/oauth2/token). IDK if that matters.

Related

Failed to connect to all addresses - gRPC with Go and NodeJS

"Failed to connect to all addresses" occurs while adding TLS certs to envoy.yaml, full error:
code: 14,
metadata: Metadata { _internal_repr: {}, flags: 0 },
details: 'failed to connect to all addresses'
Envoy config (Envoy is running on port 50000, and itemService on 50052):
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"#type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
common_tls_context:
tls_certificates:
- certificate_chain:
filename: server.cert
private_key:
filename: server.key
Client code Nodejs - (NextJS on server side - getServerSideProps)
options = {
key: readFileSync("certs/client.key"),
cert: readFileSync("certs/ca.crt"),
csr: readFileSync("certs/client.crt"),
};
const creds = credentials.createSsl(
options.cert,
options.key,
options.csr
);
grpcServer.servicesList.itemsService = new ItemsServiceClient(
"localhost:50000",
creds,
{
"grpc.ssl_target_name_override": "localhost",
"grpc.default_authority": "localhost",
}
);
Request works normally when removing TLS certs from envoy.yaml.
Error I get from grpcurl tool: Failed to dial target host "localhost:50000" x509: certificate relies on legacy Common Name field, use SANs instead.
When I set GODEBUG=x509ignoreCN=0, seems like error stays same.

AWS XRAY on Fargate service

I want to add xray to my Fargate service. Everything works (synth/deploy) but in the logs I'am seeing the following error:
2022-02-07T13:38:22Z [Error] Sending segment batch failed with:
AccessDeniedException: 2022-02-07 14:38:22status code: 403, request
id: cdc23f61-5c2e-4ede-8bda-5328e0c8ac8f
The user I'am using to deploy the application has the AWSXrayFullAccess permission.
Do I have to grant the task the permission manually? If so how?
Here is a snippet of the application:
const cdk = require('#aws-cdk/core');
const ecs = require('#aws-cdk/aws-ecs');
const ecsPatterns = require('#aws-cdk/aws-ecs-patterns');
class API extends cdk.Stack {
constructor(parent, id, props) {
super(parent, id, props);
this.apiXRayTaskDefinition = new ecs.FargateTaskDefinition(this, 'apixRay-definition', {
cpu: 256,
memoryLimitMiB: 512,
});
this.apiXRayTaskDefinition.addContainer('api', {
image: ecs.ContainerImage.fromAsset('./api'),
environment: {
"QUEUE_URL": props.queue.queueUrl,
"TABLE": props.table.tableName,
"AWS_XRAY_DAEMON_ADDRESS": "0.0.0.0:2000"
},
logging: ecs.LogDriver.awsLogs({ streamPrefix: 'api' }),
}).addPortMappings({
containerPort: 80
})
this.apiXRayTaskDefinition.addContainer('xray', {
image: ecs.ContainerImage.fromRegistry('public.ecr.aws/xray/aws-xray-daemon:latest'),
logging: ecs.LogDriver.awsLogs({ streamPrefix: 'xray' }),
}).addPortMappings({
containerPort: 2000,
protocol: ecs.Protocol.UDP,
});
// API
this.api = new ecsPatterns.ApplicationLoadBalancedFargateService(this, 'api', {
cluster: props.cluster,
taskDefinition: this.apiXRayTaskDefinition,
desiredCount: 2,
cpu: 256,
memory: 512,
createLogs: true
})
props.queue.grantSendMessages(this.api.service.taskDefinition.taskRole);
props.table.grantReadWriteData(this.api.service.taskDefinition.taskRole);
}
}
The user I'am using to deploy the application has the AWSXrayFullAccess permission.
This is irrelevant, the task will not get all the rights of the user that deploys the stack.
Yes, you need to add the required permissions to the task with
this.apiXRayTaskDefinition.taskRole.addManagedPolicy(
iam.ManagedPolicy.fromAwsManagedPolicyName('AWSXRayDaemonWriteAccess')
);
References:
AWS managed policy with required access for the X-Ray daemon: https://docs.aws.amazon.com/xray/latest/devguide/security_iam_id-based-policy-examples.html#xray-permissions-managedpolicies
Import an AWS-managed policy: https://docs.aws.amazon.com/cdk/api/v1/docs/#aws-cdk_aws-iam.ManagedPolicy.html#static-fromwbrawswbrmanagedwbrpolicywbrnamemanagedpolicyname
Access the task role: https://docs.aws.amazon.com/cdk/api/v1/docs/#aws-cdk_aws-ecs.FargateTaskDefinition.html#taskrole-1
Add a policy: https://docs.aws.amazon.com/cdk/api/v1/docs/#aws-cdk_aws-iam.IRole.html#addwbrmanagedwbrpolicypolicy

nodemailer with virtualmin and postfix - authentication failed

Im using virtual min. I have a working email account no-reply#mydomain.com
I can login through webmin / virtual min and send emails.
Now I want to send mails from node.js using nodemailer.
Btw this also works from the command line.
mail -s "testing email" mygmail#gmail.com < /dev/null
Here is my nodemailer config:
{
"smtp" : {
"host": "localhost",
"secure": false,
"auth": {
"user": "no-reply#mydomain.com",
"pass": "mypass"
},
"tls": {
"rejectUnauthorized": false
},
"debug" : true
},
"from" : "Resourceful Network <no-reply#resourcefulnetwork.nl>"
}
I found the following config in postfix config file:
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
broken_sasl_auth_clients = yes
smtpd_recipient_restrictions = permit_mynetworks permit_sasl_authenticated reject_unauth_destination
smtp_tls_security_level = may
This is all set by virtual min.
I get this error
Error: Invalid login: 535 5.7.8 Error: authentication failed: authentication failure
code: 'EAUTH',
response: '535 5.7.8 Error: authentication failed: authentication failure',
responseCode: 535,
command: 'AUTH PLAIN'
I'm 100% sure the password is correct.
If I set "secure" : true in nodemailer config I get this error
errno: 'ECONNREFUSED',
code: 'ESOCKET',
syscall: 'connect',
address: '127.0.0.1',
port: 465,
So that doesn't seem to be the way to go.
Not sure what login protocol to use.. Do I need to set authMethod?
Secure or not secure?
I was able to fix this by removing auth from the nodemailer configuration.
I now realise smtpd_recipient_restrictions = permit_mynetworks means anyone on localhost is automatically allowed to send. And somehow still provider a (correct) username + password tripped things up?

Nodemailer Invalid Login 535 when setting environment variables with ZEIT Now

When I use this configuration for Nodemailer:
const transporter = nodemailer.createTransport({
service: process.env.EMAIL_SERVICE,
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASSWORD
}
});
Variables:
EMAIL_SERVICE: 'Hotmail',
EMAIL_USER: 'MyEmail#outlook.com',
EMAIL_PASSWORD: 'MyPassword'
And I send the email like this:
transporter.sendMail({
to: email,
subject: 'Confirmar cambio de contraseƱa',
html: `Para cambiar la contraseƱa entre a ${url} <br> Este token solo dura 24 horas.`
});
In development it works perfectly but when I deploy this in Now, It throws me this error:
How can I resolve it?
Here a little example about this:
https://github.com/MontoyaAndres/NowProblemNodemailer
And here in now:
https://nowemail-owcypiqzsr.now.sh
Thank you :)
You probably need to add a secret and then add the environment variables to your now.json.
Run these commands once to add secrets in your ZEIT Now account (use now switch if you have multiple accounts).
now secret add email-service 'Hotmail'
now secret add email-user 'MyEmail#outlook.com'
now secret add email-password 'MyPassword'
Then add the env key to your now.json file like so:
{
"version": 2,
"builds": [/* your builds go here */],
"env": {
"EMAIL_SERVICE": "#email-service",
"EMAIL_USER": "#email-user",
"EMAIL_PASSWORD": "#email-password"
}
}

Regarding the error occurred in my test case in nodeload.js script

I have designed an app using elastic search. And when Iam trying to write the test case using node load.js. I have got a problem that when I increase the number users I was getting the warning that "WARN: Error during HTTP request: Error: ECONNREFUSED, Could not contact DNS servers" and Iam unable rectify the problem so please help me in solving this error.
nl.run({
name: "test",
host: 'localhost',
port: 9200,
//path: '/my_river/page/_search?q=sweden',
numUsers: 2000, //Increased my num of user**
timeLimit: 180,
targetRps: 500,
stats: [
'result-codes',
{ name: 'latency', percentiles: [0.9, 0.99] },
'concurrency',
'rps',
'uniques',
{ name: 'http-errors', successCodes: [200,404], log: 'http-errors.log' }
],

Resources