App runs fine on heroku, except for one problem - reactjs

I have an application that allows people to look up their government representatives using an address. When it is connected to a local mongo db it works perfectly. I am using the db to store like/dislikes on the reps page. The problem is that I added the documents in the db manually and so it doesn't/cant track likes on heroku, since the db isnt create dynamically in the code. That didn't seem like too much of a problem though. I would just create the exact same documents in Atlas db, right? Well here I am several hours later and Im pulling my hair out.
I have never used Atlas but it seems straightforward. Use their url to connected to your cluster/db. Thats about it from what I can tell. Problem is, I can get an empty array back from a return all route... and thats it. I have two docs in the db currently and need them to track my likes/dislikes. Im just not sure where to go from here and have to deploy this by tomorrow evening. Please help
I have reformatted the url six ways to Sunday. I have moved around code in server to try and diagnose the problem and I have messed with my routes. Ive tried postman and am getting an array back but thats it. I added a console.log in server that returns my cluster so I know Im connected in some capacity.
Sorry if Im missing something obvious and didn't show enough code, I am fairly new to all this if you cant tell. Thank you so much for any help you can provide.
````const routes = require("./routes");
````const app = express();
//////////////////////////////////////////////////////////////////
````const mongoose = require("mongoose")
````const bodyParser = require("body-parser")
````//Bodyparser middleware
````app.use(bodyParser.json());
````//DB config
````const MongoClient = require('mongodb').MongoClient;
````const uri = "mongodb+srv://NicoP93:REDACTED#cluster0-vplwy.mongodb.net/test?retryWrites=true"
````MongoClient.connect(uri, function(err, client) {
````if(err){
````console.log("Not connected to atlas")
````} else {
````console.log("connected to atlas")
````}
````const collection = client.db("Overthrow").collection("Knowledge")
````if(collection){console.log("Collection Connection")};
````console.log(collection);
````mongoose.connect(uri).then(() => {console.log("Mongoose connected")})
````})
````const items = require("./routes/api/items")
````app.use('/api/items', items);
````const PORT = process.env.PORT || 3001;
///////////////////////////////////////////////////////////////////////////////
````app.use(express.urlencoded({
````extended: true
````}));
````app.use(express.json());
````if (process.env.NODE_ENV === "production") {
````app.use(express.static("client/build"));
````}
````app.use(routes);
````app.listen(PORT, function () {
```` console.log("🌎 ==> API Server now listening on port " + PORT);
````});
When I hit the get all route I would expect something like
{"_id:5cd9fff41c9d440000f25cb8
buttonLike:0
buttonDislike:0
representativeName:"Ted Cruz"
__v:"0"}
because thats what in my atlas db, Im looking at it right now in their website.
Additionally, node server returns:
{(node:5600) DeprecationWarning: current URL string parser is deprecated, and will be removed in a future version. To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.
🌎 ==> API Server now listening on port 3001
connected to atlas
Collection Connection
Collection {
s:
{ pkFactory:
{ [Function: ObjectID]
index: 550824,
createPk: [Function: createPk],
createFromTime: [Function: createFromTime],
createFromHexString: [Function: createFromHexString],
isValid: [Function: isValid],
ObjectID: [Circular],
ObjectId: [Circular] },
db:
Db {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
s: [Object],
serverConfig: [Getter],
bufferMaxEntries: [Getter],
databaseName: [Getter] },
topology:
ReplSet {
_events: [Object],
_eventsCount: 25,
_maxListeners: Infinity,
clientInfo: [Object],
s: [Object] },
dbName: 'Overthrow',
options:
{ promiseLibrary: [Function: Promise],
readConcern: undefined,
readPreference: [ReadPreference] },
namespace: 'Overthrow.Knowledge',
readPreference: ReadPreference { mode: 'primary', tags: undefined },
slaveOk: true,
serializeFunctions: undefined,
raw: undefined,
promoteLongs: undefined,
promoteValues: undefined,
promoteBuffers: undefined,
internalHint: null,
collectionHint: null,
name: 'Knowledge',
promiseLibrary: [Function: Promise],
readConcern: undefined,
writeConcern: undefined } }
Mongoose connected }

Related

Knex not able to connect with azure-active-directory-password

I have a project using Objection.js and Knex.
I am trying to connect to a database that is hosted in Azure and uses Azure Active Directory to authenticate.
objection: 3.0.1
knex: 2.2.0
My understanding is Knex uses Tedious to build the connection to the database. Looking into the github I am referencing this document for connection properties.
https://github.com/knex/knex/blob/master/lib/dialects/mssql/index.js
const cfg = {
authentication: {
type: settings.type || 'default',
options: {
userName: settings.userName || settings.user,
password: settings.password,
domain: settings.domain,
token: settings.token,
clientId: settings.clientId,
clientSecret: settings.clientSecret,
tenantId: settings.tenantId,
msiEndpoint: settings.msiEndpoint,
},
},
server: settings.server || settings.host,
options: {
database: settings.database,
encrypt: settings.encrypt || false,
port: settings.port || 1433,
connectTimeout: settings.connectionTimeout || settings.timeout || 15000,
requestTimeout: !isNil(settings.requestTimeout)
? settings.requestTimeout
: 15000,
rowCollectionOnDone: false,
rowCollectionOnRequestCompletion: false,
useColumnNames: false,
tdsVersion: settings.options.tdsVersion || '7_4',
appName: settings.options.appName || 'knex',
trustServerCertificate: false,
...settings.options,
},
};
Looking into Tedious it looks like the connection can be setup with these settings:
https://tediousjs.github.io/tedious/api-connection.html
I have followed various troubleshooting threads but none have worked for me and the errors I am getting are not helping.
Here are what I have as my Knex initialization.
const db = knex({
client: 'mssql'
useNullAsDefault: true,
connection: {
type: 'azure-active-directory-password',
server: 'sql-db.database.windows.net',
database: 'my-db',
user: 'myUser',
password: 'myPassword',
clientId: 'myUser-AzureAd-Object-Id',
tenantId: 'myTenantId',
encrypt: true,
...knexSnakeCaseMappers()
}
})
When I try to run my migrations I get the error:
AggregateError
at c:\path\node_modules\tedious\lib\connection.js:2759:31
at processTicksAndRejections (node:internal/process/task_queues:94:5)
While running the code normally, without applying the migration first I get this error.
this.loginError = new _esAggregateError.default([new _errors.ConnectionError('Security token could not be authenticated or authorized.', 'EFEDAUTH'), err]);
At this point I do not know why it is not connecting to the database. On the same machine I can connect to the database over SSMS using Azure Active Directory - Password and the same credentials I am passing in the knexfile.
I am lost as to why this is not connecting any help would be greatly appreciated.
FIXED: I had more of an issue with the logging of Knex, after using Tedious directly the error was a bit more clear.
The issue was related to the Client ID, it says it is going to be required, but when I provided it, it said it was incorrect. Removing the Client ID fixed the issue.

How to set up heartbeats with webstomp-client and Spring Boot?

I have a React.js frontend establishing websocket connections (STOMP) with a Spring Boot backend.
I'm using the webstomp-client library on the front-end, because that is the one recommended in the Spring Boot documentation.
I can establish the connection and send and receive messages. However, there are no outgoing heartbeats from the frontend, only incoming heartbeats being sent by the backend. After some time (~ 5 mins on real project, ~30 secs on minimum reproducible example), the connection is closed with the following error:
Whoops! Lost connection to ws://localhost:8081/test:
CloseEvent {isTrusted: true, wasClean: true, code: 1002, reason: "", type: "close", …}
bubbles: false
cancelBubble: false
cancelable: false
code: 1002
composed: false
currentTarget: WebSocket {url: "ws://localhost:8081/test", readyState: 3, bufferedAmount: 0, onerror: null, onopen: ƒ, …}
defaultPrevented: false
eventPhase: 0
isTrusted: true
path: []
reason: ""
returnValue: true
srcElement: WebSocket {url: "ws://localhost:8081/test", readyState: 3, bufferedAmount: 0, onerror: null, onopen: ƒ, …}
target: WebSocket {url: "ws://localhost:8081/test", readyState: 3, bufferedAmount: 0, onerror: null, onopen: ƒ, …}
timeStamp: 49784.04500000033
type: "close"
wasClean: true
__proto__: CloseEvent
It looks like someone else was having the same problem here, but no SO response for them.
Oddly, if I set heartbeat: false on the webstomp-client initialiser, the incoming heartbeats will stop as well, but the connection remains open indefinitely... which kinda solves my problem but I'd feel a lot more comfortable if the heartbeats were working.
I've tried various combinations of heartbeat settings on the front-end and back-end, but no joy.
According to the CloseEvent specifications, the code 1002 means that "The endpoint is terminating the connection due to a protocol error." Unfortunately, I don't know how to turn that bit of info into a solution.
The webstomp-client is setup like this:
export const connectWebstompClient = () => {
const websocketsAreNotAvailableInTheBrowser = typeof WebSocket !== 'function';
let stompClient: Client
if (websocketsAreNotAvailableInTheBrowser) {
console.log('Creating SockJS fallback')
stompClient = webstomp.over(new SockJS('http://localhost:8081/test'))
} else {
console.log('Creating websocket')
stompClient = webstomp.client('ws://localhost:8081/test', {
heartbeat: { incoming: 10000, outgoing: 10000},
// heartbeat: false,
protocols: webstomp.VERSIONS.supportedProtocols()
})
}
console.log('connecting webstomp client')
stompClient.connect({/* no headers */}, () => onConnect(), (error: Frame | CloseEvent) => onError(error))
const onConnect = () => {
console.log('subscribing to /topic/test')
stompClient.subscribe('/topic/test', (payload: Message) => showTextMessage(payload))
// Puts stomp client in the Redux store
store.dispatch<any>(addClient(stompClient));
}
const onError = (error: Frame | CloseEvent) => console.error(error)
return stompClient
}
Minimum reproducible example for the front-end is here, and for the back-end here.
Can anybody see what's going on? Your help would be much appreciated.
The problem is coming from this line:
protocols: webstomp.VERSIONS.supportedProtocols()
In fact, supportedProtocols() will return the following array: ['v10.stomp', 'v11.stomp', 'v12.stomp'].
When sending the connect frame to the stomp server, webstomp-client Version 1.2.6 (which I'm examinating) will fall back to the first supportedProtocols() entry i.e v10.stomp of the stomp protocol.
Unfortunately v10.stomp does not support heartbeat and that's why your browser is not sending PING in response to the server PONG.
To fix this problem, you can set the protocols option to 'v11.stomp' or 'v12.stomp' depending on your stomp backend (spring stomp) supported version:
protocols: ["v12.stomp"]
Or if not supported by backend:
protocols: ["v11.stomp"]
I hope that my response solves your issue.

axios post request to MongoDB Atlas error 11000

I am trying to send some data to MongoDB Atlas from a React frontend. I tested the backend (an Express server) with Postman. The routes and endpoints are working as expected, and I can create todos and see them in MongoDB-Atlas.
// createTodo.js
onSubmit(e) {
e.preventDefault()
const todo = {
todoTitle: this.state.todoTitle,
todoBody: this.state.todoBody,
}
console.log(todo)
axios.post('http://localhost:5000/api/todos', todo).then((res) => console.log(res.data))
this.setState({
todoTitle: '',
todoBody: '',
})
}
the (res.data) that I am console.logging gives me an object with a MongoError 11000 code.
Object { driver: true, name: "MongoError", index: 0, code: 11000, keyPattern: {…}, keyValue: {…} }
CreateTodo.js:40
Any one have experience with this type of error? Are there any online resources or guides to help resolve this one? Thank you.
I got this error one time when i defined a collection with a particular name and later on changed the name, hence i believe that mongoDB expected to receive a data attributed to that particular name but didn't and got that error. However, i managed to fix it after dropping the collection and run again.

Identity Server 4 Silent Renew ErrorResponse: login_required

I have cloned the repo from the redux-oidc-example and it works for the most part but after a few hours it gives the following error:
Action payload: ErrorResponse: login_required
at new e (oidc-client.min.js:1)
at t [as _processSigninParams] (oidc-client.min.js:1)
at t [as validateSigninResponse] (oidc-client.min.js:1)
at oidc-client.min.js:1
UserManager.js looks like this:
const userManagerConfig = {
client_id: 'js.dev',
client_secret: 'secret',
redirect_uri: `${window.location.protocol}//${window.location.hostname}${window.location.port ? `:${window.location.port}` : ''}/callback`,
response_type: 'id_token token',
scope: 'openid email profile role offline_access',
authority: 'http://localhost:8080',
silent_redirect_uri: `${window.location.protocol}//${window.location.hostname}${window.location.port ? `:${window.location.port}` : ''}/silent_renew.html`,
automaticSilentRenew: true,
filterProtocolClaims: true,
loadUserInfo: true
};
and my identity server config:
{
"Enabled": true,
"ClientId": "js.dev",
"ClientName": "Javascript Client",
"ClientSecrets": [ { "Value": "K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=" } ],
"AllowedGrantTypes": [ "implicit", "authorization_code" ],
"AllowedScopes": [ "openid", "email", "profile", "role", "offline_access" ],
"AllowOfflineAccess": true,
"AllowAccessTokensViaBrowser":true,
"RedirectUris": [
"http://localhost:8081/callback",
"http://localhost:8081/silent_renew.html"
],
"PostLogoutRedirectUris": [
"http://localhost:8081"
],
"AccessTokenLifetime": 900,
"RequireConsent": false
}
I noticed that prior to error last valid response had one cookie response(idsrv.session) with empty value with the expiry date set to the previous year:
I believe this to be the root cause of the issue, I searched it on related Github repo and tried to add the Cookie.SameSite to none but it didn't help:
services.AddAuthentication()
.AddSaml(Configuration,externalProviders.UseSaml)
.AddCookie(options => {
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromDays(30);
options.Cookie.SameSite = SameSiteMode.None;
});
Any idea!
This is likely due to your IDP session expiring - if you call the authorize endpoint with prompt=none but it's unable to satisfy that request because no valid session exists (i.e. authentication cookie does not exist or has expired) then it will return error=login_required.
If this occurs then the correct course of action is to do an interactive (i.e. prompt=login) sign in request in the top level browser window.
After searching the Identity Server 4 repo, I made the following changes to my code:
services.AddIdentityServer(options=>
{
options.Authentication.CookieLifetime = TimeSpan.FromDays(30);
options.Authentication.CookieSlidingExpiration = true;
})
.AddProfileService<ProfileService>()
.AddSigningCertificate(Configuration)
.AddInMemoryClients(Configuration.GetSection("IdentityServer:Clients"))
.AddInMemoryIdentityResources(Resources.GetIdentityResources());
It started working afterward, but you would have to login again after you close the browser or reopen a new tab I guess it's because of the sessionStorage.
When the session expires the signin-callback is being called by STS having a query parameter called 'error' with the value 'login_required'.
In the signin-callback, before completing sign-in, you can check for this query parameter and if it's found you can sign-out also from your web client.
I had the same issue and tried the proposed above, but for me, it actually was SameSiteMode not set correctly on IdentityServer Cookies. It caused Callback error: ErrorResponse: login_required right after login and after N attempts user was logged out.
This helped me https://github.com/IdentityServer/IdentityServer4/blob/main/src/IdentityServer4/host/Extensions/SameSiteHandlingExtensions.cs
What they do is based on this article https://devblogs.microsoft.com/dotnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/
Hope this is useful.
Update.
I had another issue related to this when the user was logged out after re-opening a browser (especially on Android Chrome). login_required error was shown. I noticed that session cookie Expires/Max-Age was set to Session and not some future date. Probably because of that check session iframe (with src={identity server url}/connect/checksession) failed as Identity Server thought there was no session as cookie expired.
I tried setting cookie lifetime via options, but it didn't work as expected for some reason. Lifetime was always 14 days:
services.AddIdentityServer(options=>
options.Authentication.CookieLifetime = TimeSpan.FromDays(30);
options.Authentication.CookieSlidingExpiration = true;
})
Then I tried this and it worked for me:
services.ConfigureApplicationCookie(options => {
options.ExpireTimeSpan = sessionCookieLifetime;
options.SlidingExpiration = true;
})

Firebase: how to require auth to list all nodes, yet allow anonymous read/write to individual nodes?

I'm writing an invitation application, and would like to email individual people unique URLs, e.g.
http://www.example.com/invitation.html?inviteID=-Jkbw6ycU7ZUOipmqlb5
The HTML app contains JavaScript that connects to a particular Firebase, looking up a node by the inviteID from the URL. Example:
https://my-firebase-123#firebaseio.com/-Jkbw6ycU7ZUOipmqlb5
Each top-level node looks roughly like
-Jkbw6ycU7ZUOipmqlb5: {
email: 'joe#gmail.com',
people: [
{name: 'Joe', accept: true},
{name: 'Jane', accept: false}
],
comments: 'Jane can't make it, but I'm looking forward to it!'
}
This already works great! But I'm having trouble understanding how to properly secure the data. I need the recipients to continue to be able to access those URLs without authentication - anyone who supplies a node ID can read and write to that node and its children - and yet I need to require auth to see the Firebase at its top level, so that invitees cannot see (or modify!) anyone else's responses without knowing other inviteIDs. How can I do this?
{
"rules": {
".read": ??
".write": ??
}
}
I expect both .read and .write will need a rule that means something like this:
"You requested a specific child node, not the top level node; otherwise you must be an authorized user (auth != null) to see the top level node."
The app is written in ReactJS and communicates with Firebase roughly like this:
componentWillMount: function() {
var dbAddress = 'my-firebase-123#firebaseio.com/';
this.firebaseRef = new Firebase(dbAddress + this.props.inviteId);
this.firebaseRef.on("value", function(dataSnapshot) {
this.setState(dataSnapshot.val());
}.bind(this));
},
onSend: function() {
this.firebaseRef.set(this.state);
},
I have been reading the various firebase docs trying to find a similar solution.
Assuming your firebase json structure is something like the following:
{ Invitations: {
-Jkbw6ycU7ZUOipmqlb5: {
email: 'joe#gmail.com',
people: [
{name: 'Joe', accept: true},
{name: 'Jane', accept: false}
],
comments: 'Jane can't make it, but I'm looking forward to it!'
}
-Jkbw6ycU7ZUOipmqlb6: {
... another invitation ...
}
-Jkbw6ycU7ZUOipmqlb7: {
... another invitation ...
}
}
I came up with the following security config which appears to do what you require:
{
"rules": {
".read": false,
".write": false,
"invitations": {
"$inviteid": {
".read": true,
".write": true
}
}
}
}
Actually the top level read/write false may be inferred because if I set the config as the following it seems to work in the same way:
{
"rules": {
"invitations": {
"$inviteid": {
".read": true,
".write": true
}
}
}
}
Now I cant seem to be able to browse the invitations as in if I try and mount at the following points I get permission denied (assuming your firebase address is https://my-firebase-123#firebaseio.com/:
this.firebaseRef = new Firebase('https://my-firebase-123#firebaseio.com/');
this.firebaseRef = new Firebase('https://my-firebase-123#firebaseio.com/invitations');
where as mounting at the following level lets me in:
this.firebaseRef = new Firebase('https://my-firebase-123#firebaseio.com/invitations/-Jkbw6ycU7ZUOipmqlb5');
Not sure if what I have done is actually achieving your requirements from a security perspective (i.e. is it actually secure?).
Would appreciate any feedback from the expert firebase community on this approach.

Resources