GUNjs user not getting authenticated in incognito window - reactjs

I have a very strange issue. I have a login page in React where users can either Sign-Up or Login .
When the user signs up, I create a user in GUNjs. And when I login, it logins successfully. However, when I open the site in incognito mode and try to login with the same creds, it says that the user doesn't exist. I also have a peer server running. The peer server saves user's data, but still this happens.
My code:
decentralized_db.ts
import GUN from "gun";
import "gun/sea";
import "gun/axe";
export const db = GUN({
peers: ["http://localhost:8765/gun"],
});
export const user = db.user().recall({ sessionStorage: true });
peerServer.js
gun = (Gun = require("gun"))({
web: require("http").createServer(Gun.serve(__dirname)).listen(8765),
});
My React component:
loginPage.tsx:
import { useState } from "react";
import { user } from "../decentralized_db";
export default function LoginPage() {
const [userCred, setUserCred] = useState({
username: "",
password: "",
});
function login(event: any) {
event.preventDefault();
user.auth(
userCred.username,
userCred.password,
({ err }: any) => err && alert(err)
);
console.log(user);
}
function signup(event: any) {
event.preventDefault();
user.create(userCred.username, userCred.password, ({ err }: any) => {
if (err) {
alert(err);
} else {
login(event);
}
});
}
function handleChange(event: any) {
let name = event.target.name;
let value = event.target.value;
let clonedState: any = { ...userCred };
clonedState[name] = value;
setUserCred(clonedState);
}
return (
<form>
<label htmlFor="username">Username</label>
<input
onChange={handleChange}
name="username"
value={userCred.username}
minLength={3}
maxLength={16}
/>
<label htmlFor="password">Password</label>
<input
onChange={handleChange}
name="password"
value={userCred.password}
type="password"
/>
<button className="login" onClick={login}>
Login
</button>
<button className="login" onClick={signup}>
Sign Up
</button>
</form>
);
}

#pranava-mohan hey this is a bug, there was a race condition in the profile syncing - the login would check credentials before the profile finished loading.
To confirm this is the same bug as you are experiencing: Try to click login multiple times on the other page. Does it login on later attempts, even if you changed nothing? Then this was the bug.
Working to have this fixed in future versions. Until then, the best place to get help with bugs is http://chat.gun.eco we reply faster there.
Sorry about that! Please comment if it winds up being something else.

Related

Why when using React useState is the payload data clearing on a onsubmit event? This is a POST request to the server side

Background: I am trying to get my frontend code to connect with my backend code.
Steps Taken:
--set a breakpoint for on click event for the submit button: this returns with the event being fired however no Promise function ran and no response in the console.
--tested sending data on Postman to backend and get status 200 and works (backend is set correctly and is hosted on heroku)
--cleaned up POST request and set it as a promise and stored in Const Variable
Steps the code is taking:
First taking in string in each input field (name, email, question).
Using react usestate hook to use the current state and set the event.target.value as those values.
Once that is stored, then it gets stored in a variable called custData
One submit button is clicked it calls on the Variable that stores the Promise function for POST request using Axios
(This is where I believe something else is occurring)
*the POST is set to the correct backend URL with the values of custData which should be a string. Then it should return with response.data in the console and does not.
Below is my frontend react code for this component:
import React from 'react';
import {useState} from 'react';
import Axios from 'axios'
//import { response } from 'express';
const QuoteForm = () => {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [question, setQuestion] = useState("");
//This is the promise version
const custData =
{
"name" :name ,
"email" :email ,
"question":question
} ;
const submitPromise= () => {
console.log(custData);
Axios.post('https://hookahsite-backend.herokuapp.com || https://localhost:8000 ' , custData)
.then( (axiosResponse)=> {
// here you can use the data
console.log(custData);
const submitQuestions = axiosResponse.data;
console.log(submitQuestions);
})
.catch((e)=> {console.log(e)})
}
//this uses try catch however the backend is not getting hit with any data
//tested this same request in Postman and it works
/*
function submitQuestion() {
try {
Axios.post('https://hookahsite-backend.herokuapp.com ' ,
{
name:name ,
email:email ,
question:question
},
)
}
catch (err) {console.error(err);}
}
*/
return(
<React.Fragment>
<form id="quoteForm"
>
<h1 id="quoteTitle">Quote Help Form</h1>
<p id="quotePar">Please provide your Name, Contact Email, and what products you would like more information about in this form :</p>
<label id="formName" className="Form">
Name:
<input type="text" name="name"
onChange={(event) => { setName(event.target.value);}}
/>
</label>
<label id="formEmail" className="Form">
Email:
<input type="text" name="email"
onChange={(event) => { setEmail(event.target.value);
}}/>
</label>
<br/>
<label id="formQuestion" className="Form" >
What products would you like to know more about:
<input type="text" name="help"
onChange={(event) => { setQuestion(event.target.value);
}}/>
</label>
<br/>
<br/>
<button id="quoteSubmit" type="submit"
onClick =
{
submitPromise
}
/*
old way
{()=>
submitQuestion()
}
*/
>Submit </button>
</form>
›
</React.Fragment>
)
};
export default QuoteForm;
(When I set the breakpoints this is a screen shot showing the promise is there and there seems to be an issue with data being sent as possible text and not json format)
**Any Further Help on this topic would greatly be appreciated. **
The problem I believe lies in your Axios post call.
Axios is used like this:
Axios.post('ENDPOINT_URL', BODY)
Here ENDPOINT_URL is the URL of your api endpoint that you want to send a post request to, however you are saying to axios that the endpoint url is:
'https://hookahsite-backend.herokuapp.com || https://localhost:8000'
As far as I am concerned there is no logic in axios so it is trying to hit that string literally with a post request.
You should move your OR logic to other place of the application.
For example using env variables to know that you are running locally or on heroku you could do something like:
let url;
if (process.env.SERVER_LOCATION === "LOCAL") {
url = "https://localhost:8000";
} else{
url = "https://hookahsite-backend.herokuapp.com";
}
axios.post(url, custData).then( // etc

Trying to make website to update googlesheets

im trying to create a form to update googlesheets the project is in REACTJS, i followed this guide https://medium.com/#mheavers/storing-form-data-from-a-website-in-google-spreadsheets-using-javascript-react-a15aeb9143cb
now im facing a problem where i get the following error
error: Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.
my code:
import React,{useState,useEffect} from 'react'
import {Form,Text} from 'informed'
import {gapi} from 'gapi-script'
const Forms=()=>{
const [SignInStatus, setSignInStatus] = useState("")
const SPREADSHEETID= ***my spreadsheet id**
const apiKey= ***my api key***
const clientID=***my oauth id***
const SCOPE=['https://www.googleapis.com/auth/spreadsheets']
const initClient=()=>{
console.log("got key")
gapi.client.init({
'apiKey': apiKey,
'clientId':clientID,
'scope': SCOPE,
'discoveryDocs': ['https://sheets.googleapis.com/$discovery/rest?version=v4'],
}).then(()=>{
gapi.auth2.getAuthInstance().isSignedIn.listen(SignInStatus)
setSignInStatus(gapi.auth2.getAuthInstance().isSignedIn.get())
})
}
const handleclientLoad=()=>{
gapi.load('client:auth2',initClient)
}
useEffect(() => {
handleclientLoad()
}, [])
const onFormSubmit=(submissionValues)=>{
console.log(submissionValues)
const params={
spreadsheetId:SPREADSHEETID,
clientId: clientID,
range:'Sheet1',
valueInputOption:'RAW',
insetDataOption:'INSERT_ROWS'
}
const valueRangeBody={
'majorDimension':'ROWS',
'VALUES':[submissionValues]
}
let request=gapi.client.sheets.spreadsheets.values.append(params,valueRangeBody)
request.then(function(response){
console.log(response.result)
},function (reason){
console.log(reason.result.error)
console.log("error: " + reason.result.error.message)
})
}
return(
<Form onSubmit={onFormSubmit}>
<label>
first name:
<Text field="name"/>
</label>
<button type="submit">Submit</button>
</Form>
)
}
export default Forms
This might not be what you want, but forms.google.com does exactly this already.

Meteor: insert failed: Access denied. No allow validators set on restricted collection for method 'insert'

Hello I am working on a Meteor-React project and fairly new in this. I am trying to insert new object into existing collection but it shows error as indicated in the title.
This is my component responsible for inserting new item at the UI level.
import React, { useState, useEffect } from "react";
import newTask from '../api/create'
export default Create = () => {
const [task, createTask] = useState('');
handleKeyPress = (event) => {
if(event.key === 'Enter'){
newTask(task)
}
}
return (
<form className="add-task" noValidate="">
<div>
<div className="fieldset add-task-input fieldset-stripped">
<div className="fieldset-content">
<label className="fieldset-label">
<span className="fieldset-label-content has-icon">
<i className="icon-plus" />
</span>
<input
className=""
name="title"
placeholder="Add new task"
type="text"
autoComplete="off"
value={task}
onChange={(e) => { createTask(e.target.value)}}
onKeyPress={this.handleKeyPress}
/>
</label>
</div>
</div>
</div>
</form>
)
}
This is the method that I am trying to export and import into the component file above.
import { Tasks } from '../../tasks';
export default newTask = (taskTitle) => Tasks.insert({
title: taskTitle,
dueDate: null,
repeat: {},
color: '#4e42c3',
status: 'incomplete',
customFields: []
})
I have tried using methods proposed by others as below, by adding the code below into the method file above:
Tasks.allow({
insert: function () {
return true;
}
})
But still it does not work and show the same error. Any idea how to enable inserting new item into the Mongo Collection ?
The allow/deny is opening a security vulnerability. You may rather use it only for prototyping / mocking some initial software prototypes.
The correct way to update collections is to use Meteor methods:
server
import { Tasks } from '../path/to/tasks'
Meteor.methods({
'insertTask': function ({ title, dueDate, repeat, color, status, customFields }) {
return Tasks.insert({ title, dueDate, repeat, color, status, customFields })
}
})
client
import { Tasks } from '../../tasks';
export default newTask = (taskTitle) => Meteor.call('insertTask', {
title: taskTitle,
dueDate: null,
repeat: {},
color: '#4e42c3',
status: 'incomplete',
customFields: []
})
Edit: If you only want to insert the document at the UI level and never have a collection on the server stored, you may make the Task collection local by exporting it wihtout a name:
client
export const Tasks = new Mongo.Collection(null)
Are you using autopublish package?
Your trying to insert the object from the client side. Without autopublish package you can't do it. Either add autopublish or insert from the server side.
So previously I added the code below
Tasks.allow({
insert: function () {
return true;
}
})
at the client side. Once I moved it to server side, it solves my problem.

Passing correct properties to popup notification

I want to show a notification with the upload status. I took over a project in React & ASP.NET and I am relatively new to this. The question is quite simple, yet I am struggling to solve it: How do I display a popup notification showing which files have been successfully been uploaded and which not?
import * as React from "react";
import { connect } from "react-redux";
import { Form, Select, Button, Upload, message, notification} from 'antd';
import * as Actions from "../actions";
const FormItem = Form.Item;
class UploadFileForm extends React.Component<any, any> {
constructor(props: any) {
super(props);
}
handleSubmit = (e) => {
message.config({ top: 0 });
message.loading('Importing in progress...', 3);
e.preventDefault();
this.props.uploadFile(this.props.form.getFieldsValue());
notification["info"]({
message: 'Files successfully uploaded',
description: '', // <-- this line has to be modified
duration: 10
});
}
render() {
const { getFieldDecorator } = this.props.form;
return (
<Form onSubmit={this.handleSubmit}>
<FormItem label="File" >
{getFieldDecorator('upload', {
valuePropName: 'fileList',
getValueFromEvent: (e) => e.fileList.slice(-1)
})(
<Upload name="importFile" action={' '} multiple={false}>
<Button> Upload </Button>
</Upload>
)}
</FormItem>
<Button type="primary" htmlType="submit">Import</Button>
</Form>
);
}
}
export default Form.create()(UploadFileForm);
More specifically: How do I have to modify the line description: '', to show me a list of all uploaded files and their status as pure text, e.g. File(s) '1.txt', '2.txt', and '3.txt' have been successfully uploaded. File(s) '4.txt' failed.?
The project documentation says that we are using Redux-Saga, but I am not so maybe that makes the story easier.
I guess your this.props.uploadFile method is a promise so considering that you should show notification once that promise is resolved
this.props.uploadFile(this.props.form.getFieldsValue()).then(result => {
// since your client doesn't know which ones are success/failed, server should return
// this information when request is finished
const { successUploads, failUploads } = result;
notification["info"]({
message: 'Files successfully uploaded',
description: `File(s) ${successUploads.join(', ')} have been successfully uploaded. File(s) ${failUploads.join(', ')} failed.`
duration: 10
});
});
If you can't control whats returned from the server then you'd need to track uploads on client side, but that would mean having multiple uploads (requests) to the server and your upload method would look something like this:
async function uploadFiles(files) {
// I've called your server upload uploadService.send(), but replace this with your method
const results = await Promise.all(
files.map(file => uploadService.send(file))
.map(p => p.catch(e => e)
);
let successUploads = [];
let failUploads = [];
results.forEach((result, idx) => {
const file = files[idx];
if (result instanceof Error) {
failUploads.push(file);
} else {
successUploads.push(file);
}
});
return {
successUploads,
failUploads
}
}
Then you could call uploadFiles same way as shown in first snippet.

React redirect after login not working

I am trying using react-router and redux to build my react app. However, I am unable to get the url to route back to the dashboard post login. Can somebody please point out my mistakes?
const form = reduxForm({
form: 'login'
});
class Login extends Component {
handleFormSubmit(formProps) {
this.props.loginUser(formProps);
var token = cookie.load('token');
if(token !== undefined){
const location = this.props.location;
if (location.state && location.state.nextPathname) {
browserHistory.push(location.state.nextPathname)
} else {
browserHistory.push('/')
}
}
}
renderAlert() {
if(this.props.errorMessage) {
return (
<div>
<span><strong>Error!</strong> Authentication error</span>
</div>
);
}
}
render() {
const { handleSubmit } = this.props;
return (
<div>
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
{this.renderAlert()}
<div>
<label>Username</label>
<Field name="username" className="form-control" component="input" type="text" />
</div>
<div>
<label>Password</label>
<Field name="password" className="form-control" component="input" type="password" />
</div>
<button type="submit" className="btn btn-primary submitButton">Login</button>
</form>
</div>
);
}
}
function mapStateToProps(state) {
return {
errorMessage: state.auth.error,
message: state.auth.message
};
}
const mapDispatchToProps = dispatch => ({
loginUser: () =>
dispatch(loginUser);
});
export default connect(mapStateToProps, mapDispatchToProps)(form(Login));
My loginUser function is as below:
export function loginUser({ username, password }) {
return function(dispatch){
axios.post(`${AUTH_URL}/obtain-auth-token/`, { username, password}, {
headers: {
"X-CSRFToken": cookie.load('csrftoken')
}
})
.then(response => {
if('token' in response.data){
cookie.save('token', response.data.token, {path: '/'});
dispatch({type: AUTH_USER});
} else{
console.log("Error condiction: " + response);
errorHandler(dispatch, error.response, AUTH_ERROR);
}
})
.catch((error) => {
errorHandler(dispatch, error.response, AUTH_ERROR);
});
}
}
This is my first react-redux project so the mistake might be pretty elementary. Would really appreciate your help!
The root of your issue appears to be with handling your async call - in your handleFormSubmit(formProps) function you have the following two lines:
this.props.loginUser(formProps);
var token = cookie.load('token');
You are dispatching your action that will be running your async function (loginUser(formProps) does a post using axios), and then you immediately try to consume the results of this async function by loading the token that it should have stored in a cookie upon success. This does not work because immediately upon running an async function, JavaScript will not wait for the results but instead will return to your handleFormSubmit function and run the rest of it through completion. I am sure that if you console.log'd your token it will be undefined (assuming there were no cookies before running the app) - the function continued without waiting for your async function.
So I know two good options that you can use to fix this:
Simple, standard solution: You run dispatch({type: AUTH_USER}); upon the success of your async post - have this action creator result in a change in your state held by redux (ex: loginFlag). Include loginFlag as a prop in your Login component (include it in your mapStateToProps function). Finally, include a componentWillReceiveProps(nextProps) lifecycle function to your Login component and have it handle the route change. Include something like:
componetWillReceiveProps(nextProps) {
if (nextProps.loginFlag) {
var token = cookie.load('token');
if(token !== undefined){
const location = this.props.location;
if (location.state && location.state.nextPathname) {
browserHistory.push(location.state.nextPathname)
} else {
browserHistory.push('/')
}
}
}
}
Or:
Heavier solution using another package: You can use the npm package react-router-redux (https://github.com/reactjs/react-router-redux), to do a push to browserHistory within your redux action at the end of your async call - doing so will require you to setup its middleware and hook it up to your redux store. More info: https://github.com/reactjs/react-router-redux#api
I would definitely recommend looking into react-router-redux. If you do not want the browserHistory functions available in your redux actions, then it is very simple to setup in your project (don't need to make changes to the store or add its middleware) and it allows you to track your url path within your redux state which can be very helpful.

Resources