Nodemailer with Gmail on Loopback error - Object #<Object> has no method 'getToken' - node.js

I am learning Loopback and I decided to make some email sending. I want to use gmail account.
I created remote method and configured datasources. Here is how it looks:
"myEmailDataSource": {
"name": "myEmailDataSource",
"connector": "mail",
"transports": [
{
"type": "smtp",
"host": "smtp.gmail.com",
"auth": {
"xoauth2": {
"user": "myMail#gmail.com",
"clientId": "myClientId.apps.googleusercontent.com",
"clientSecret": "mySecret",
"refreshToken": "myToken"
}
}
}
]
}
But when I want to send an email, it throws this error:
TypeError: Object #<Object> has no method 'getToken'
at SMTPConnection._handleXOauth2Token (/home/arth95/Projects/firstCMS/node_modules/loopback/node_modules/nodemailer/node_modules/nodemailer-smtp-transport/node_modules/smtp-connection/src/smtp-connection.js:961:67)
at SMTPConnection.login (/home/arth95/Projects/firstCMS/node_modules/loopback/node_modules/nodemailer/node_modules/nodemailer-smtp-transport/node_modules/smtp-connection/src/smtp-connection.js:233:18)
at SMTPTransport.<anonymous> (/home/arth95/Projects/firstCMS/node_modules/loopback/node_modules/nodemailer/node_modules/nodemailer-smtp-transport/src/smtp-transport.js:96:24)
at SMTPConnection.g (events.js:180:16)
at SMTPConnection.EventEmitter.emit (events.js:92:17)
at SMTPConnection._actionEHLO (/home/arth95/Projects/firstCMS/node_modules/loopback/node_modules/nodemailer/node_modules/nodemailer-smtp-transport/node_modules/smtp-connection/src/smtp-connection.js:692:10)
at SMTPConnection._processResponse (/home/arth95/Projects/firstCMS/node_modules/loopback/node_modules/nodemailer/node_modules/nodemailer-smtp-transport/node_modules/smtp-connection/src/smtp-connection.js:511:16)
at SMTPConnection._onData (/home/arth95/Projects/firstCMS/node_modules/loopback/node_modules/nodemailer/node_modules/nodemailer-smtp-transport/node_modules/smtp-connection/src/smtp-connection.js:357:10)
at CleartextStream.EventEmitter.emit (events.js:95:17)
at CleartextStream.<anonymous> (_stream_readable.js:746:14)
Why is that?

I had exact same problem. Did you find any solution for this?
As a workaround I've done following.
create a boot script in server\boot
in the script wrote following code
var email = app.models.Email;
var auth = email.dataSource.connector.transports[0].transporter.options.auth;
auth.xoauth2 = require('xoauth2').createXOAuth2Generator(auth.xoauth2);
This converts the xoauth2 object that you defined in data source to XOAuth2Generator object that is needed by nodemailer.
You need to have xoauth2 module installed.
There should be a better way to handle this. But so far I've not found it, so using this workaround.

Related

Issue with Keycloak and nestjs

I have been trying to include Keycloak authentication with my NestJS app and this is driving me crazy. I keep getting an error
"WARN [Keycloak] Cannot validate access token: Error: Grant validation failed. Reason: failed to load public key to verify token. Reason: connect ECONNREFUSED ::1:8080"
My Keycloak.json file is:
{
"realm": "my-realm",
"auth-server-url": "http://localhost:8080/",
"ssl-required": "external",
"resource": "test",
"verify-token-audience": false,
"credentials": {
"secret": "my-secret"
},
"policy-enforcer": {}
}
This is being imported in Apps.module.ts as:
KeycloakConnectModule.register('./dist/keycloak.json', {
policyEnforcement: PolicyEnforcementMode.PERMISSIVE,
tokenValidation: TokenValidation.ONLINE,
}),
I am using Keycloak version 19.0.1 and nest-key cloak-connect v 1.9.0.
When I tried debugging. Grant-manager.js's public key is undefined. I checked with the well-known config and jwks-uri was defined as:
http://localhost:8080/realms/my-realm/protocol/openid-connect/certs
Any ideas on what might be wrong?

Cypress dynamic setCookie options

Having a problem setting options of cookies using variables, we use cookies and passwords that need to be secure, ie not in a repo so we store them in an auth.json file that we have engineers author in their respective implementations from a secure document. So we pull that data from the document and I am attempting to write the cookie dynamically as such:
const auth = require('../../fixtures/auth')
context('Assertions', () => {
beforeEach(() => {
cy.task('log', JSON.stringify(auth.cookies[0].path))
cy.setCookie(`'${auth.cookies[0].key}'`, `'${auth.cookies[0].value}'`,{ path: `/`} )
// cy.setCookie(auth.cookies[1].key, auth.cookies[1].value, {"path": auth.cookies[1].path} )
// cy.setCookie(auth.cookies[2].key, auth.cookies[2].value, {"path": auth.cookies[2].path} )
})
The above works when I hardcode the path, but when I try to pull the path in from the json using:
cy.setCookie(`'${auth.cookies[0].key}'`, `'${auth.cookies[0].value}'`,{ path: `'${auth.cookies[0].path}'`} )
I get this error:
The json looks like this:
"cookies": [
{"key": "da_key", "value": "da_value", "path": "da_path", "name": "da_name"},
...
I've tried all sorts of combinations of single, double quotes, JSON.stringify, etc. Nothing seems to work except hardcoding. I've looked all over the cypress site for an examples of dynamically passing cookies, esp with options, but to no avail. We will need to dynamically set path and names for cookies for other features we test so it would be helpful to know how to do this.
Using cypress ^8.6.0 and chrome 94
** Edit: Adding stack trace:
at <unknown (http://localhost:58166/__cypress/runner/cypress_runner.js:153781:77)From previous event:
at Context.setCookie (http://localhost:58166/__cypress/runner/cypress_runner.js:153970:16)
From Your Spec Code:
at Context.eval (webpack:///./cypress/integration/3-qa-sanity/clinique-sanity.spec.js:6:12)
From Node.js Internals:
Error: true
at Chrome._handleMessage (/Users/rhowk/Library/Caches/Cypress/8.7.0/Cypress.app/Contents/Resources/app/packages/server/node_modules/chrome-remote-interface/lib/chrome.js:256:18)
at WebSocket.<anonymous> (/Users/rhowk/Library/Caches/Cypress/8.7.0/Cypress.app/Contents/Resources/app/packages/server/node_modules/chrome-remote-interface/lib/chrome.js:234:23)
at WebSocket.emit (events.js:376:21)
at Receiver.receiverOnMessage (/Users/rhowk/Library/Caches/Cypress/8.7.0/Cypress.app/Contents/Resources/app/packages/server/node_modules/ws/lib/websocket.js:825:21)
at Receiver.emit (events.js:376:21)
at Receiver.dataMessage (/Users/rhowk/Library/Caches/Cypress/8.7.0/Cypress.app/Contents/Resources/app/packages/server/node_modules/ws/lib/receiver.js:437:15)
at <unknown> (/Users/rhowk/Library/Caches/Cypress/8.7.0/Cypress.app/Contents/Resources/app/packages/server/node_modules/ws/lib/receiver.js:394:24)
at <unknown> (/Users/rhowk/Library/Caches/Cypress/8.7.0/Cypress.app/Contents/Resources/app/packages/server/node_modules/ws/lib/permessage-deflate.js:308:10)
at <unknown> (/Users/rhowk/Library/Caches/Cypress/8.7.0/Cypress.app/Contents/Resources/app/packages/server/node_modules/ws/lib/permessage-deflate.js:391:8)
at afterWrite (internal/streams/writable.js:466:6)
at onwrite (internal/streams/writable.js:446:8)
at InflateRaw.afterTransform (internal/streams/transform.js:103:4)
at Zlib.processCallback (zlib.js:586:9)
From previous event:
at $Cypress.automation (http://localhost:58166/__cypress/runner/cypress_runner.js:166441:13)
at automate (http://localhost:58166/__cypress/runner/cypress_runner.js:153732:23)
at automateCookies (http://localhost:58166/__cypress/runner/cypress_runner.js:153746:13)
at Context.setCookie (http://localhost:58166/__cypress/runner/cypress_runner.js:153967:15)
at <unknown> (http://localhost:58166/__cypress/runner/cypress_runner.js:168605:18)
From previous event:
at runCommand (http://localhost:58166/__cypress/runner/cypress_runner.js:168584:11)
at next (http://localhost:58166/__cypress/runner/cypress_runner.js:168727:17)
From previous event:
at <unknown> (http://localhost:58166/__cypress/runner/cypress_runner.js:182911:80)
From previous event:
at Object.run (http://localhost:58166/__cypress/runner/cypress_runner.js:182906:24)
at Object.run (http://localhost:58166/__cypress/runner/cypress_runner.js:168782:18)
at $Cy.cy.<computed> [as setCookie] (http://localhost:58166/__cypress/runner/cypress_runner.js:169952:20)
at Context.runnable.fn (http://localhost:58166/__cypress/runner/cypress_runner.js:170176:24)
at callFn (http://localhost:58166/__cypress/runner/cypress_runner.js:122963:22)
at Hook.../driver/node_modules/mocha/lib/runnable.js.Runnable.run (http://localhost:58166/__cypress/runner/cypress_runner.js:122950:8)
at <unknown> (http://localhost:58166/__cypress/runner/cypress_runner.js:176739:31)
From previous event:
at Object.onRunnableRun (http://localhost:58166/__cypress/runner/cypress_runner.js:176724:20)
at $Cypress.action (http://localhost:58166/__cypress/runner/cypress_runner.js:166256:29)
at Hook.Runnable.run (http://localhost:58166/__cypress/runner/cypress_runner.js:174428:14)
at next (http://localhost:58166/__cypress/runner/cypress_runner.js:123465:11)
at <unknown> (http://localhost:58166/__cypress/runner/cypress_runner.js:123509:6)
at timeslice (http://localhost:58166/__cypress/runner/cypress_runner.js:117435:28
Here are some refs to relevant functions in the cypress_runner.js webpack:
LN 153730
return automateCookies('set:cookie', cookie, options._log, options.timeout).then(pickCookieProps).then(resp => {
options.cookie = resp;
return resp;
}).catch(handleBackendError('setCookie', 'setting the requested cookie in', onFail));
},
LN 153889
setCookie(name, value, options = {}) {
const userOptions = options;
options = lodash__WEBPACK_IMPORTED_MODULE_0___default.a.defaults({}, userOptions, {
name,
value,
path: '/',
secure: false,
httpOnly: false,
log: true,
expiry: _cypress_utils__WEBPACK_IMPORTED_MODULE_2__[/* default */ "a"].addTwentyYears(),
timeout: config('responseTimeout')
});
const cookie = pickCookieProps(options)
This is a tough one to debug for me, but I am new to cypress so unfamiliar with how everything hangs together right now.
You should name the file auth.json and require it with the extension specified so that correct conversions can happen behind the scenes.
const auth = require('../../fixtures/auth.json')
/cypress/fixtures/auth.json
{
"cookies": [
{"key": "da_key", "value": "da_value", "path": "da_path", "name": "da_name"}
]
}

TypeError : i is not a function (AWS Lambda wrapped with epsagon)

This error is popped up on a lambda when I've upgraded got npm module to 11.5.x from 9.6.0.
I'm using serverlessframework to develop and deploy micro services. Using Epsagon wrapper for better monitoring.
I've been struggling with this error from the past 3 days. Any help would be well appreciated.
AWS lambda runtime: Node 10.x
following are a few npm packages
"serverless-webpack": "^5.3.3",
"terser-webpack-plugin": "^4.1.0",
"webpack": "^4.44.1"
"epsagon": "^1.82.0",
"got": "^11.6.0", (with got 9.0.6, there is no issue)
While I can't paste the entire code snippet, following is a snippet. If I use the same code in a simple index.js file and run it then I cant reproduce this issue.
const { body } = await got(path, {
headers: { 'custom-key': customKey },
responseType: 'json',
method: 'POST',
json: { ts: i1599227703000 },
});
The following log snippet is from cloudWatch.
{
"errorType": "TypeError",
"errorMessage": "i is not a function",
"stack": [
"TypeError: i is not a function",
" at ClientRequest.patchedCallback (/opt/nodejs/node_modules/epsagon/dist/bundle.js:1:60626)",
" at Object.onceWrapper (events.js:286:20)",
" at ClientRequest.emit (events.js:203:15)",
" at ClientRequest.EventEmitter.emit (domain.js:448:20)",
" at ClientRequest.e.emit.f [as emit] (/var/task/epsagon_handlers/abcdNameChanged-epsagon.js:2:1080726)",
" at HTTPParser.parserOnIncomingClient [as onIncoming] (_http_client.js:565:21)",
" at HTTPParser.parserOnHeadersComplete (_http_common.js:111:17)",
" at TLSSocket.socketOnData (_http_client.js:451:20)",
" at TLSSocket.emit (events.js:198:13)",
" at TLSSocket.EventEmitter.emit (domain.js:448:20)"
]
}
Finally I could crack it.
If we enable epsagon auto-tracing thru epsagon webapp, it actually adds a layer called epsagon-node-layer to that lambda.
Disabling auto-tracing helped to not to get this error.
For more details, refer to: https://github.com/epsagon/epsagon-node/issues/295

LoopbackJS version 3.x `Connection fails: MongoError: Authentication failed.`

I am trying to follow up the tutorial, I created a new app with lb, added CoffeeShop mode, then added a datasource, mongodb.
My MongoDB instance is on my local mac, and authorization is turned off.
I just run it with mongod command, no extra params, and there are no addition configs.
this is my datasources.json
{
"corp1": {
"host": "127.0.0.1",
"port": 27017,
"url": "",
"database": "devdb",
"password": "devpassword",
"name": "corp1",
"user": "devuser",
"connector": "mongodb"
}
}
I even created devdb database, and given devdb user to all databases as admin.
And still getting this error.
Connection fails: MongoError: Authentication failed.
It will be retried for the next request.
/Users/hazimdikenli/learn/loopback-getting-started/node_modules/mongodb/lib/mongo_client.js:462
throw err
^
MongoError: Authentication failed.
at Function.MongoError.create (/Users/hazimdikenli/learn/loopback-getting-started/node_modules/mongodb-core/lib/error.js:31:11)
at /Users/hazimdikenli/learn/loopback-getting-started/node_modules/mongodb-core/lib/connection/pool.js:497:72
at authenticateStragglers (/Users/hazimdikenli/learn/loopback-getting-started/node_modules/mongodb-core/lib/connection/pool.js:443:16)
at Connection.messageHandler (/Users/hazimdikenli/learn/loopback-getting-started/node_modules/mongodb-core/lib/connection/pool.js:477:5)
at Socket.<anonymous> (/Users/hazimdikenli/learn/loopback-getting-started/node_modules/mongodb-core/lib/connection/connection.js:333:22)
at emitOne (events.js:116:13)
at Socket.emit (events.js:211:7)
at addChunk (_stream_readable.js:263:12)
at readableAddChunk (_stream_readable.js:250:11)
at Socket.Readable.push (_stream_readable.js:208:10)
at TCP.onread (net.js:597:20)
It am thinking that this is a newbie issue but I cannot find the problem. So please help me resolve this issue.
Although I am not running mongod with --auth option, it still turned out to be an authentication issue, it worked after I executed following.
db.createUser({
user : "devuser",
pwd : "devpassword",
roles: [ {role: "dbOwner", db: "devdb"},
{role: "readWrite", db: "devdb"}]
})
before this, I granted roles on "admin" with this script, but looks like it was not enough.
role: "userAdminAnyDatabase", db: "admin"

Mongoose bulkWrite - Wrong type for 'q'

For my test suite, I want to bulkWrite test info in the database, and then bulk delete any of the test info entered throughout the test to come back to a clean slate. I do so by running a bulkWrite on the db to which I pass the content of a JSON file loaded via nodeJS's require statement.
The problem is that for the dataset
[ { deleteOne: { username: 'test-author' } } ]
passed to models[key].collection.bulkWrite(action[key]), where key is the name of the model of interest and action is the JSON file,I get the following error:
{ MongoError: Wrong type for 'q'. Expected a object, got a null.
at Function.MongoError.create (/var/www/website/server/node_modules/mongodb-core/lib/error.js:31:11)
at /var/www/website/server/node_modules/mongodb-core/lib/connection/pool.js:483:72
at authenticateStragglers (/var/www/website/server/node_modules/mongodb-core/lib/connection/pool.js:429:16)
at Connection.messageHandler (/var/www/website/server/node_modules/mongodb-core/lib/connection/pool.js:463:5)
at Socket.<anonymous> (/var/www/website/server/node_modules/mongodb-core/lib/connection/connection.js:339:20)
at emitOne (events.js:96:13)
at Socket.emit (events.js:188:7)
at readableAddChunk (_stream_readable.js:176:18)
at Socket.Readable.push (_stream_readable.js:134:10)
at TCP.onread (net.js:548:20)
name: 'MongoError',
message: 'Wrong type for \'q\'. Expected a object, got a null.',
ok: 0,
errmsg: 'Wrong type for \'q\'. Expected a object, got a null.',
code: 14,
codeName: 'TypeMismatch' }
I have done some research and have been unable to find a solution to this problem. The error itself is pretty meaningless, so I can't grasp much out of it. Any idea how to solve the problem?
Any help would be greatly appreciated!
Cheers!
As per the MongoDB API, the deleteOne, deleteMany, updateOne, updateMany, replaceOne, and replaceMany operation requires to have a property filter which acts as the filter for the query.
However Mongoose's API shows the following (mistaken) example:
Character.bulkWrite([
...
{
deleteOne: {
{ name: 'Eddard Stark' }
}
}
]).then(handleResult);
Hence, the data sent over changes from:
[{
"deleteOne": { "username": "test-author" }
}]
to
[{
"deleteOne": { "filter": { "username": "test-author" }}
}]
I'll make sure to pass the message along to the mongoosejs dev group.

Resources