I have a Django rest API with JWT auth for signup and login built with react.
When trying to log in a user I get a 403 forbidden error.
I added the csrf token to the headers of the request and I can see it in the promise when using the console, so that's not the problem here.
I just don't understand where this post is breaking
import axios from "axios";
import Cookies from "js-cookie";
var csrftoken = Cookies.get("csrftoken");
const axiosInstance = axios.create({
baseURL: "http://127.0.0.1:8000/api/",
timeout: 5000,
headers: {
HTTP_X_CSRF_TOKEN: csrftoken,
Authorization: localStorage.getItem("access_token")
? "JWT " + localStorage.getItem("access_token")
: null,
"Content-Type": "application/json",
accept: "application/json",
withCredentials: true,
},
});
axiosInstance.interceptors.response.use(
(response) => response,
(error) => {
const originalRequest = error.config;
// test for token presence, no point in sending a request if token isn't present
if (
localStorage.getItem("refresh_token") &&
error.response.status === 401 &&
error.response.statusText === "Unauthorized"
) {
const refresh_token = localStorage.getItem("refresh_token");
return axiosInstance
.post("/token/refresh/", { refresh: refresh_token })
.then((response) => {
localStorage.setItem("access_token", response.data.access);
localStorage.setItem("refresh_token", response.data.refresh);
axiosInstance.defaults.headers["Authorization"] =
"JWT " + response.data.access;
originalRequest.headers["Authorization"] =
"JWT " + response.data.access;
return axiosInstance(originalRequest);
})
.catch((err) => {
console.log(err);
});
}
// specific error handling done elsewhere
return Promise.reject({ ...error });
}
);
export default axiosInstance;
And the login component using the axios instance:
import React, { Component } from "react";
import axiosInstance from "../axiosApi";
import DjangoCSRFToken from "django-react-csrftoken";
class Login extends Component {
constructor(props) {
super(props);
this.state = { username: "", password: "" };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ [event.target.name]: event.target.value });
}
async handleSubmit(event) {
event.preventDefault();
try {
const response = await axiosInstance.post("/token/obtain/", {
username: this.state.username,
password: this.state.password,
});
axiosInstance.defaults.headers["Authorization"] =
"JWT " + response.data.access;
localStorage.setItem("access_token", response.data.access);
localStorage.setItem("refresh_token", response.data.refresh);
return response;
} catch (error) {
throw error;
}
}
render() {
return (
<div>
Login
<form onSubmit={this.handleSubmit}>
<DjangoCSRFToken />
<label>
Username:
<input
name="username"
type="text"
value={this.state.username}
onChange={this.handleChange}
/>
</label>
<label>
Password:
<input
name="password"
type="password"
value={this.state.password}
onChange={this.handleChange}
/>
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
export default Login;
the console when hitting submit:
Please try removing CSRF from the call. and make use of #csrf_exepmt for further information please follow this link. Django csrf_exempt using your JWT it's enough.
Example:
#csrf_exempt
def myEndpoint():
// my code
Related
I'm trying to get all user data from the backend to display on the webpage. However, the getAllUsers() seems to not send back a response as the console.logs are not printed out.
Here is my ViewUsers.js
import React, { Component } from "react";
import AdminServices from "../Services/AdminServices";
import "././ViewUsers.css"
const ViewUsersComponent = (users) => {
return (
<div className="viewusers">
<h1>All users</h1>
<div className="viewusers-list">
{users.map((user) => {
return (
<React.Fragment>
<p> <b>Name</b> : {user.username} </p>
<p> <b>Email</b> : {user.email} </p>
<p> <b>Website role</b> : {user.websiteRole} </p>
<hr />
</React.Fragment>
)
})}
</div>
</div>
)
}
export default class ViewUsers extends Component {
constructor(props) {
super(props);
this.retrieveUsers = this.retrieveUsers.bind(this);
this.state = {
users: []
}
}
componentDidMount() {
this.retrieveUsers();
}
retrieveUsers() {
AdminServices.getAllUsers()
.then(response => {
if (response && response.data) {
this.setState({
users: response.data
});
}
console.log(response.data);
console.log('DATA RECEIVED')
})
.catch(e => {
console.log('ERROR')
console.log(e);
});
}
render () {
const { users } = this.state;
console.log(users)
if (Array.isArray(users) && users.length) {
return ViewUsersComponent(users)
} else {
return (
window.location = '/notfound'
)
}
}
}
This is the AdminServices.js
import http from "../http-common"
class AdminServices {
getAllUsers() {
return http.get("/users");
}
getAllProjects() {
return http.get("/projects");
}
}
export default new AdminServices();
And the http-common.js
import axios from "axios";
export default axios.create({
baseURL: "http://localhost:8080",
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*"
},
mode: "cors",
credentials: "include",
withCredentials: true
});
And the userRoute.js
const express = require('express');
const User = require('../models/user').userModel;
const userRouter = express.Router();
// get all users
userRouter.get('/', async (req, res) => {
try {
console.log("loading users")
users = await User.find();
if (users == null) {
res.status(404).json({ message: "users not found" });
}
res.send(users);
} catch (err) {
res.status(500).json({ message: err.message });
}
});
When I send a get request to the user route via rest api, it works so I am not sure why it does not go through on the frontend
Please add CORS on backend project. You can configure the UI domain or use ‘*’ to allow all domain.
I have a Django rest API with JWT authentication and React for the front.
When trying to log in a user, my request get's a 403 where it should return a token pair.
import React, { Component } from "react";
import axiosInstance from "../axiosApi";
class Login extends Component {
constructor(props) {
super(props);
this.state = { username: "", password: "" };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleSubmitWThen = this.handleSubmitWThen.bind(this);
}
handleChange(event) {
this.setState({ [event.target.name]: event.target.value });
}
handleSubmit(event) {
event.preventDefault();
try {
const response = axiosInstance.post("/token/obtain/", {
username: this.state.username,
password: this.state.password,
});
axiosInstance.defaults.headers["Authorization"] =
"JWT " + response.data.access;
localStorage.setItem("access_token", response.data.access);
localStorage.setItem("refresh_token", response.data.refresh);
return data;
} catch (error) {
throw error;
}
}
handleSubmitWThen(event) {
event.preventDefault();
axiosInstance
.post("/token/obtain/", {
username: this.state.username,
password: this.state.password,
})
.then((result) => {
axiosInstance.defaults.headers["Authorization"] =
"JWT " + result.data.access;
localStorage.setItem("access_token", result.data.access);
localStorage.setItem("refresh_token", result.data.refresh);
})
.catch((error) => {
throw error;
});
}
render() {
return (
<div>
Login
<form onSubmit={this.handleSubmit}>
<label>
Username:
<input
name="username"
type="text"
value={this.state.username}
onChange={this.handleChange}
/>
</label>
<label>
Password:
<input
name="password"
type="password"
value={this.state.password}
onChange={this.handleChange}
/>
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
export default Login;
And the axios code:
import axios from "axios";
const axiosInstance = axios.create({
baseURL: "http://127.0.0.1:8000/api/",
timeout: 5000,
headers: {
Authorization: "JWT " + localStorage.getItem("access_token"),
"Content-Type": "application/json",
accept: "application/json",
},
});
export default axiosInstance;
the error when I hit the submit button on the login page.
login.js:68 Uncaught TypeError: Cannot read property 'access' of undefined
at Login.handleSubmit (login.js:68)
at HTMLUnknownElement.callCallback (react-dom.development.js:188)
at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
at invokeGuardedCallback (react-dom.development.js:292)
at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:306)
at executeDispatch (react-dom.development.js:389)
at executeDispatchesInOrder (react-dom.development.js:414)
at executeDispatchesAndRelease (react-dom.development.js:3278)
at executeDispatchesAndReleaseTopLevel (react-dom.development.js:3287)
I understand there's something wrong with my handlesubmit function that the call is not being made.
printing reponse.data to the console:
class Login ...
handleSubmit(event) {
event.preventDefault();
try {
const response = axiosInstance.post("/token/obtain/", {
username: this.state.username,
password: this.state.password,
});
console.log(response.data)
...
Results in a rejected Promise:
I want to translate an angularjs login page to reactjs. I did the code but I want to know if it's the right way to do it and if it's correct till what I have done. Also I want to add the dashboard page which can be accessed only authentication and user will be redirected to dashboard after login.
$scope.login=function(){
$scope.newuser={
'password':$scope.signinpassword,
'email':$scope.emailid
}
return $http.post('/api/authenticate',$scope.newuser).then(function(response,status){
if(response.data=='redirect'){
$window.location.href="/dashboard";
}else if(response.data=='verifyemail'){
angular.element(document.querySelector('#verifyemailbtn')).click();
}else if(response.data=='Invalid Password'){
window.alert("Incorrect Password");
$scope.error='Failed to authenticate'
}
});
}
my reactjs code that I translated so far
class Login extends Component {
constructor(props){
super(props);
this.state={
emailid:'',
password:''
}
}
performLogin = async (event) => {
var body={
"emailid":"this.state.emailid",
"password":"this.state.password"
}
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json"
},
body: body
};
const url = "api/login";
try {
const response = await fetch(url, options);
const result = await response.json();
console.log(`login successful`);
} catch (error) {
console.error("login credentials wrong");
}
};
render() {
return (
<div>
<input type="text"
placeholder="emailid"
onChange = {(event,newValue) => this.setState({emailid:newValue})}
/>
<br/>
<input
type="password"
placeholder="Enter your Password"
onChange = {(event,newValue) => this.setState({password:newValue})}
/>
<br/>
<button type="submit" onClick={(event) => this.performLogin(event)}/>
</div>
);
}
}
export default Login;
your code approaching is ok but have some syntax issues.
1- your need to bind "this" for methods in constructor
2- remove double quotation in performLogin for this.state.emailid
3- you don't need many functions for each input you can just handle all input feilds just with one function like so.
i refactor your code :-)
class Login extends Component {
constructor(props){
super(props);
this.performLogin = this.performLogin.bind(this)
this.handleChange = this.handleChange.bind(this)
this.state={
emailid:'',
password:''
}
}
performLogin = async (event) => {
const { enteredText } = this.state;
var body={
"emailid":this.state.emailid,
"password":this.state.password
}
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json"
},
body: body
};
const url = "api/login";
try {
const response = await fetch(url, options);
const result = await response.json();
$window.location.href="/dashboard";
} catch (error) {
console.error("login credentials wrong");
}
};
handleChange(e) {
this.setState({[e.target.name]:e.target.value})
}
render() {
return (
<div>
<input type="text"
placeholder="emailid"
onChange = {handleChange}
/>
<br/>
<input
type="password"
placeholder="Enter your Password"
onChange = {handleChange}
/>
<br/>
<button type="submit" onClick={this.performLogin}/>
</div>
);
}
}
export default Login;
for redirecting you can use lib like React-Router or just replace console.log(login successful) with $window.location.href="/dashboard"
I've been trying to create a login page but I'm getting this error called "TypeError: Failed to fetch". I have no idea where I did wrong, I checked a few StackOverflow answers, like I tried adding the event.preventdefault as I saw in one of StackOverflow answer but that didn't help.
Could someone point out the part where I'm doing wrong?
Also note: it's working fine in postman with the API. I tried the same email and password there and it's working fine in postman but getting the error with the react website.
import React, { Component } from "react";
class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: ""
};
this.onchange = this.onchange.bind(this);
}
onchange(e) {
this.setState({ [e.target.name]: e.target.value });
console.log(this.state);
}
performLogin = async event => {
event.preventDefault();
console.log("button clicked");
var body = {
password: "this.state.password",
email: "this.state.email"
};
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json"
},
body: JSON.stringify(body)
};
const url = "/api/authenticate";
try {
const response = await fetch(url, options);
const result = await response.json();
console.log(result);
console.log(`login successful`);
window.alert("login succesful");
} catch (error) {
console.error(error);
}
};
render() {
return (
<div>
<input type="text" placeholder="emailid" onChange={this.onchange} />
<br />
<input
type="password"
placeholder="Enter your Password"
onChange={this.onchange}
/>
<br />
<button type="submit" onClick={event => this.performLogin(event)}>
Submit
</button>
</div>
);
}
}
export default Login;
one anamoly is that in postman, it accepts only when I post like I've shown below
{
"password":"pass",
"email":"admin"
}
If I remove the quotes then it gives me bad string error in postman
but in codesandbox, the quotes are automatically removed when I save the sandbox. could it be because of that or is it nothing to worry about?
First you need to give your inputs name attribute to be able to correctly update the state onChange like this:
<input type="text" name="email" placeholder="emailid" onChange={this.onchange} /> <br />
<input type="password" name="password" placeholder="Enter your Password" onChange={this.onchange}/>
Secondly, you need to create the request body like this:
var body = {
password: this.state.password,
email: this.state.email
};
And lastly, you need to check if fetch response is ok, because in 4xx errors fetch does not give error.
So with all these changes, your component code must be like this:
import React, { Component } from "react";
class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: ""
};
this.onchange = this.onchange.bind(this);
}
onchange(e) {
this.setState({ [e.target.name]: e.target.value });
}
performLogin = async event => {
event.preventDefault();
var body = {
password: this.state.password,
email: this.state.email
};
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json"
},
body: JSON.stringify(body)
};
const url = "/api/authenticate";
try {
const response = await fetch(url, options);
const result = await response.json();
console.log(result);
if (response.ok) {
console.log("login successful");
window.alert("login succesful");
} else {
console.log("login failed");
window.alert("login failed");
}
} catch (error) {
console.error(error);
}
};
render() {
return (
<div>
<input
type="text"
name="email"
placeholder="emailid"
onChange={this.onchange}
/>
<br />
<input
type="password"
name="password"
placeholder="Enter your Password"
onChange={this.onchange}
/>
<br />
<button type="submit" onClick={event => this.performLogin(event)}>
Submit
</button>
<hr />
State: {JSON.stringify(this.state)}
</div>
);
}
}
export default Login;
But these fixes in react app will not be enough, because codesandbox uses https and login api is using http. This will give the following error:
Mixed Content: The page at 'https://hkj22.csb.app/Login' was loaded
over HTTPS, but requested an insecure resource
'http://***/api/authenticate'. This request has been
blocked; the content must be served over HTTPS.
And only way to resolve this problem seems to use https for the api as described in this answer.
You can contact api owner to host his api using https.
Im noob with React and trying to create a login form. When the user enters correct login informations a rest api will return JWT token. However i cant find how to set the state "token" to be the value of the response. Still i can save the response.token to localstorage and it shows correctly. I think the "this" points to wrong function, but how could i get around this without breaking the logic? Any hints? Thanks
Login.js
import React from 'react';
import axios from 'axios';
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
import MyInput from './../components/Input';
import { connect } from "react-redux";
import { Form } from 'formsy-react';
var server = "http://localhost:3000";
class Login extends React.Component {
constructor(props) {
super(props)
this.state = {
canSubmit: false,
token: null
};
this.enableButton = this.enableButton.bind(this);
this.disableButton = this.disableButton.bind(this);
this.submit = this.submit.bind(this);
}
submit(data) {
axios.post(server + '/login', {
username: data.username,
password: data.password
})
.then(function (response) {
if (response.data.token) {
var token = response.data.token;
localStorage.setItem("token", token);
this.setState({ token: token }); //Here is the problem
console.log(this.state.token);
} else {
location.reload();
}
});
}
enableButton() {
this.setState({ canSubmit: true })
}
disableButton() {
this.setState({ canSubmit: false })
}
render() {
return (
<div className="loginForm">
<h2 className="page-header">Login</h2>
<Form onSubmit={this.submit} onValid={this.enableButton} onInvalid={this.disableButton}>
<div className="form-group">
<label>Username: </label>
<MyInput name="username" validations="isExisty" validationError="Required" required />
</div>
<div class="form-group">
<label>Password: </label>
<MyInput type="password" name="password" validations="isExisty" validationError="Required" required />
</div>
<button
type="submit"
className="btn btn-primary"
disabled={!this.state.canSubmit}>
Login
</button>
</Form>
<br/>
Create user!
<a id="forgot" href="forgot">Forgot password?</a>
</div>);
}
}
export default Login;
You can use an arrow function to keep the context of submit and be able to access to setState:
submit(data) {
axios.post(server + '/login', {
username: data.username,
password: data.password
})
.then((response) => {
if (response.data.token) {
var token = response.data.token;
localStorage.setItem("token", token);
this.setState({ token: token });
console.log(this.state.token);
} else {
location.reload();
}
});
}
As Alexander stated in the comment, I must use the arrow function => or set this for callback.