React-gatsby login authentioncation failed - reactjs

I am using Gatsby for my app. I have created one api from Mock api. My api looks like this. I have made one post-request for login, when user will put his/her email and password, if it does not match then it will alert "failed login" or if it is success it will alert ("successfully login") and navigate to successful page. But what ever email and password I am putting it always shows me I login successfully which is wrong logic. The email should be like my api's email: alak#gmail.com" and password: test123 . I think my logic right but still I am making the mistake. I share my code in Codesandbox.
PS: Codesandbox is based on react. but logic is same as my below code
Here is my code:
import React, { ReactElement, useState } from 'react';
import { PageProps, navigate } from 'gatsby';
import styled from 'styled-components';
import MainTemplate from '../templates/index';
import { TextInput } from '../components/textInput';
import { Button } from '../components/buttons';
import { API_URLS } from '../utilities';
interface Props extends PageProps {
}
export default function SignIn({ }: Props): ReactElement {
const [state, setState] = useState({
"email": ``,
"password": ``,
"loading": false
});
const { loading } = state;
const signInValue = (e) => {
setState({
...state,
[e.target.id]: e.target.value
});
};
const onSubmit = async (e) => {
e.preventDefault();
console.log(state);
setState({
"loading": true,
...state
});
const response = await fetch(`https://run.mocky.io/v3/beec46b8-8536-4cb1-9304-48e96d341461`, {
"method": `POST`,
"headers": {
"Accept": `application/json`,
'Content-Type': `application/json`
},
"body": { state }
});
if (response.ok) {
alert(`you have suceefully login`);
navigate(`/success`, { "state": { email } });
} else {
alert(`login failed`);
}
};
return (
<MainTemplate>
<TextInput
type="text"
value={state.email}
onChange={signInValue}
id="email"
required
/>
<TextInput
type="password"
value={state.password}
onChange={signInValue}
id="password"
required
/>
<Button
type="submit"
name="action"
onClick={onSubmit}
disabled={loading}
> {loading ? `loading...` : `save`}
</Button>
</MainTemplate>
);
}

According to the fetch specification:
The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500. Instead, it will resolve normally (with ok status set to false), and it will only reject on network failure or if anything prevented the request from completing.
So your api might be returning an error but response.ok might still be true. You should check response.status !== 200 instead.
Update: works as shown below based on your sandbox:

Related

Apollo `useMutation()` stuck loading inside Next.js component

The following form component in Next.js submits without errors but never completes the mutation. Instead it get's stuck in the loading state.
import React, { useState } from "react";
import { gql, useMutation } from "#apollo/client";
import { ApolloClient, InMemoryCache } from "#apollo/client";
const client = new ApolloClient({
ssrMode: true,
uri: process.env.API_URL,
cache: new InMemoryCache(),
});
const CREATE_COMMENT = gql`
mutation CreateComment(
$username: String!
$comment: String!
) {
createComment(
data: { username: $username, comment: $comment }
) {
id
username
comment
}
}
`;
export default function SocialForm() {
const [commentName, updateCommentName] = useState("");
const [commentDescription, updateCommentDescription] = useState("");
const [createComment, { data, error, loading }] = useMutation(
CREATE_COMMENT,
{ client }
);
const handleFormSubmit = async () => {
await createComment({
variables: {
name: commentName,
comment: commentDescription
},
});
};
if (loading) return "loading...";
if (error) return <p>error text :(</p>;
return (
<>
<form
onSubmit={async (e) => {
e.preventDefault();
await handleFormSubmit();
}}
className="social-form"
>
<input
required
onChange={(e) => updateCommentName(e.target.value)}
type="text"
placeholder="Full Name"
className=""
/>
<textarea
required
maxLength="280"
onChange={(e) => updateCommentDescription(e.target.value)}
className="w-full"
name="comment"
rows="5"
placeholder="Leave your thoughts and images at the moment"
/>
</form>
</>
);
}
Also the server side mutation with this exact same schema runs correctly inside Apollo playground and I'm using Keystone.js to auto generate the schema.
There's an error in your example:
const handleFormSubmit = async () => {
await createComment({
variables: {
name: commentName, // <-- Shouldn't this be `username: commentName,`?
comment: commentDescription
},
});
};
Otherwise, when you say the form "submits without errors but never completes", I assume you mean, from your browser? Can you see the GraphQL request being made in the browsers developer tools? I don't know which one you're in but there should be a way to see the requests being made and their responses. If the GraphQL server is returning an error, you'll see it there.
Also, what is Keystone logging on the command line during all this? If you add some console.log() statements to your custom mutation, do they get called?

best way to authenticate with SWR (firebase auth)

I'm doing project with React , firebase auth social signin(google, github provider) and backend(spring boot)
I'm wondering how can i use useSWR for global state for google userData
Here's my Code This is Login page simply i coded
In this page, I fetch userData(email, nickname ,, etc) with header's idToken(received from firebase auth) and backend validates idToken and send me a response about userData
This is not problem I guess.. But
// import GithubLogin from '#src/components/GithubLogin';
import GoogleLogin from '#src/components/GoogleLogin';
import { auth, signOut } from '#src/service/firebase';
import { fetcherWithToken } from '#src/utils/fetcher';
import React, { useEffect, useState } from 'react';
import useSWR from 'swr';
const Login = () => {
const [token, setToken] = useState<string | undefined>('');
const { data: userData, error } = useSWR(['/api/user/me', token], fetcherWithToken);
useEffect(() => {
auth.onAuthStateChanged(async (firebaseUser) => {
const token = await firebaseUser?.getIdToken();
sessionStorage.setItem('user', token!);
setToken(token);
});
}, []);
return (
<div>
<button onClick={signOut}>Logout</button>
<h2>Login Page</h2>
<GoogleLogin />
</div>
);
};
export default Login;
Here's Code about fetcher using in useSWR parameter
export const fetcherWithToken = async (url: string, token: string) => {
await axios
.get(url, {
headers: {
Authorization: `Bearer ${token}`,
Content-Type: 'application/json',
},
withCredentials: true,
})
.then((res) => res.data)
.catch((err) => {
if (err) {
throw new Error('There is error on your site');
}
});
};
problem
I want to use userData from useSWR("/api/user/me", fetcherWithToken) in other page! (ex : Profile Page, header's Logout button visibility)
But for doing this, I have to pass idToken (Bearer ${token}) every single time i use useSWR for userData. const { data: userData, error } = useSWR(['/api/user/me', token], fetcherWithToken);
Like this.
What is the best way to use useSWR with header's token to use data in other pages too?
seriously, I'm considering using recoil, context api too.
but I don't want to.
You can make SWR calls reusable by wrapping them with a custom hook. See the SWR docs page below.
Make It Reusable
When building a web app, you might need to reuse the data in many
places of the UI. It is incredibly easy to create reusable data hooks
on top of SWR:
function useUser (id) {
const { data, error } = useSWR(`/api/user/${id}`, fetcher)
return {
user: data,
isLoading: !error && !data,
isError: error
}
}
And use it in your components:
function Avatar ({ id }) {
const { user, isLoading, isError } = useUser(id)
if (isLoading) return <Spinner />
if (isError) return <Error />
return <img src={user.avatar} />
}

React axios post request does not send the data

I am using react for my app. I am learning post request. I found one dummy api site Mocky where I can test my post request. This is my api link .For post request I used axios. I don't know how the Mocky api works. I made post request. when I console log the input values I can the value.But when I console log the response it seems like it does not get the data. I don't see any where I am making mistake.
Here is my code:
import React, { useState } from 'react';
import { API_URLS } from '../utilities';
import axios from "axios";
export default function CreateAccount() {
const [state, setState] = useState({
"email": ``,
"password": ``,
"loading": false,
"error": ``
});
const onChangeStudent = (e) => {
setState({
...state,
[e.target.id]: e.target.value
});
};
const onSubmit = async (e) => {
e.preventDefault();
console.log(state);
const url = `https://run.mocky.io/v3/15c2b7ec-9f31-4a18-ae60-a7f41e1f39b2`;
const obj = {
"email": state.email,
"password": state.password
};
console.log(obj.email); //I can see the input value
console.log(obj.password);//I can see the input value
axios
.post(url, obj)
.then((res) => {
console.log(res.data); // it does not show the data
console.log(res);
})
.catch((error) => {
setState({
...state,
"error": error
});
});
};
return (
<div>
<form onSubmit={onSubmit}>
<input
type="text"
value={state.name}
onChange={onChangeStudent}
id="email"
required
/>
<input
type="password"
value={state.password}
onChange={onChangeStudent}
id="password"
required
/>
<button
className="btn waves-effect blue lighten-1"
type="submit"
name="action"
disabled={state.loading}
>
{state.loading ? `loading...` : `save`}
</button>
</form>
</div>
);
}
Hi can't seem to find anything wrong with what you are doing.
I tested the below and it worked for me. Try to change from .then to await. Hope this solves your problem. Check in your network tab if your request is successful and if you are sending the body.
try {
const response = await axios.post('https://run.mocky.io/v3/4b95050f-2bcc-4c78-b86e-6cac09372dce', data);
console.log("Response", response);
} catch(e) {
console.error(e);
}

Losing JWT token after browser refresh

I'm learning Full Stack Development with Spring Boot 2.0 and React .
The authentication and authorization are managed by JWT and the app works as expected except I have to re-login after I refresh the browser.
How to maintain JWT token even after browser refresh ?
import React, { Component } from 'react';
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
import Snackbar from '#material-ui/core/Snackbar';
import Carlist from './Carlist';
import {SERVER_URL} from '../constants.js';
class Login extends Component {
constructor(props) {
super(props);
this.state = {username: '', password: '', isAuthenticated: false, open: false};
}
logout = () => {
sessionStorage.removeItem("jwt");
this.setState({isAuthenticated: false});
}
login = () => {
const user = {username: this.state.username, password: this.state.password};
fetch(SERVER_URL + 'login', {
method: 'POST',
body: JSON.stringify(user)
})
.then(res => {
const jwtToken = res.headers.get('Authorization');
if (jwtToken !== null) {
sessionStorage.setItem("jwt", jwtToken);
this.setState({isAuthenticated: true});
}
else {
this.setState({open: true}); // maintient snackbar ouvert
}
})
.catch(err => console.error(err))
}
handleChange = (event) => {
this.setState({[event.target.name] : event.target.value});
}
handleClose = (event) => {
this.setState({ open: false });
}
render() {
if (this.state.isAuthenticated === true) {
return (<Carlist />)
}
else {
return (
<div>
<br/>
<TextField tpye="text" name="username" placeholder="Username"
onChange={this.handleChange} /><br/>
<TextField type="password" name="password" placeholder="Password"
onChange={this.handleChange} /><br /><br/>
<Button variant="raised" color="primary" onClick={this.login}>Login</Button>
<Snackbar
open={this.state.open} onClose={this.handleClose}
autoHideDuration={1500} message='Check your username and password' />
</div>
);
}
}
}
export default Login;
I think you simply don't check for the token in local storage in your constructor. When you reload the page, your constructor executes and sets isAuthenticated = false, whether there is a token in local storage or not. You should add additional logic to check the token in local storage before finally setting isAuthenticated. Probably the best place to put this code would be componentDidMount() function. I mean set it initially to false and then update in componentDidMount() according to current authorization status. Have a look at my GitHub, I have a small boilerplate project with such auth flow set-up. Hope this helps, happy coding!
I would use local storage instead of session storage like this
localStorage.setItem("jwt", jwtToken)
instead of the line
sessionStorage.setItem("jwt", jwtToken);
The check the local storage in the dev console, refresh the page and see if it is still there. It may require some other changes in your auth flow to build it off localStorage instead of sessionStorage; however, this will solve the immediate problem of losing the jwt on page refresh.

Axios.post returning :ERR_EMPTY_RESPONSE after API call

I've made a axios post request in my react component, which makes a request to twilio to send a text message to my phone from a route on my server.
The text message and payload are transmitted successfully however when opening the network tab in the console I get this error in a minute or two.
POST http://localhost:8080/api/twilio net::ERR_EMPTY_RESPONSE
Any notion out there how to solve this?
This is the code from my react component:
import React, { Component } from 'react';
import axios from 'axios';
import { Grid, Segment, Form } from 'semantic-ui-react';
import './test.css';
class Test extends Component {
constructor(props) {
super(props);
this.state = { phonenumber: '' };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ phonenumber: event.target.value });
}
handleSubmit() {
return axios
.post('/api/twilio', {
phonenumber: this.state.phonenumber,
})
.then(resp => resp.data)
.catch(error => console.log(error));
}
render() {
const { phonenumber } = this.state;
console.log('phonenumber', phonenumber);
return (
<Grid columns={1} stackable textAlign="center">
<Grid.Column width={1} />
<Grid.Column width={14}>
<Form onSubmit={this.handleSubmit}>
<Segment stacked>
<Form.Group id="form-group" inline>
<label>Phone Number</label>
<Form.Input onChange={this.handleChange} value={phonenumber} placeholder="+12223334444" />
</Form.Group>
<Form.Button id="form-group-button" content="Submit" />
</Segment>
</Form>
</Grid.Column>
<Grid.Column width={1} />
</Grid>
);
}
}
export default Test;
Update:
This is the twilio route on the backend.
const router = require('express').Router();
module.exports = router;
router.post('/', (req, res) => {
let SID = 'ACc5b16ad0cefc3b514e69bc30636726e2';
let TOKEN = '3145fb41afe308f22b0b7c647e6a8e17';
let SENDER = '+18622256079';
if (!SID || !TOKEN) {
return res.json({ message: 'add TWILIO_SID and TWILIO_TOKEN to .env file.' });
}
let client = require('twilio')(SID, TOKEN);
client.messages
.create({
to: req.body.phonenumber,
from: SENDER,
body: 'This is the ship that made the Kessel Run in fourteen parsecs?',
})
.then(message => console.log(message.sid));
});
In your route on the server, nothing will ever be returned to the client because SID and TOKEN are always defined (at least in your example). To make sure that the request will not fail, you will need to send at least some response back after the Twilio request, e.g.:
client.messages
.create({
to: req.body.phonenumber,
from: SENDER,
body: 'This is the ship that made the Kessel Run in fourteen parsecs?'
})
.then(message => {
console.log(message.sid);
// Either just send an empty, successful response or some data (e.g. the `sid`)
res.status(200).send(message.sid);
})
.catch(err => {
console.log(err);
// In case of an error, let the client know as well.
res.status(500).send(err);
});

Resources