React.js JWT Socket.io Authentication - reactjs

I am trying to use Socket.io in React with an Express Backend, which using JWT (Passport JWT).
In my usual routes I have no problems with my Auth.
The Authorization header is sent (Authorization Bearer Token) and checked on the backend, however I simply do not get how to include the Bearer Token when sending data from React Client.
I have looked up the extraHeaders option but simply could not get them to work.
Here is my Code:
class Chat extends React.Component {
constructor(props) {
super(props);
this.state = {
username: "",
message: "",
messages: []
};
this.socket = io("localhost:5000", {
extraHeaders: {
Authorization: "My Bearer authorization_token_here"
}
});
this.socket.on("RECEIVE_MESSAGE", function(data) {
addMessage(data);
});
const addMessage = data => {
console.log(data);
this.setState({ messages: [...this.state.messages, data] });
console.log(this.state.messages);
};
this.sendMessage = ev => {
ev.preventDefault();
this.socket.emit("SEND_MESSAGE", {
author: this.state.username,
message: this.state.message
});
this.setState({ message: "" });
};
}
render() {jsx code here}
The socket connection is set up, however I have no idea how to get the token to the server. Ideally I'd send a ID in the token, decode it on the server and lookup more info about the user in my DB.
Happy Holidays and I'm thankfull for any help or hints how to solve my problem.

As per, the documentation extraHeaders will only work in browsers if transport option is set to polling.
so enable polling on serverside and use the following snippet
io({
transportOptions: {
polling: {
extraHeaders: { "Authorization": "My Bearer authorization_token_here" }
},
}
});

Related

Send current logged in user from Django backend to React frontend using axios

I am trying to send current logged in username from django backend to React frontend. I have created an endpoint currentuser/ that works perfectly fine in backend, it returns the expected result but when I call this api endpoint in React using axios,null value is returned there.
Here's the code for backend
#view.py
from django.contrib.auth import get_user_model
from rest_framework import serializers
from rest_framework.response import Response
from rest_framework.views import APIView
User = get_user_model()
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username')
class LoggedInUserView(APIView):
def get(self, request):
serializer = UserSerializer(request.user)
return Response(serializer.data)
#urls.py
urlpatterns = [
path('currentuser/', views.LoggedInUserView.as_view(), name='currentuser'),
]
Here's the result when calling the api directly
Here's the code for frontend
class App extends React.Component {
state = {
users: [],
}
getUsers() {
axios.defaults.headers.common['Content-Type'] = 'application/json';
axios.get(`http://localhost:8000/currentuser/`)
.then(res => {
console.log("res :", res);
const user = res.data;
console.log("response from backend", user);
this.setState({ users: user });
console.log(this.state.users);
})
.catch(err => {
console.log("error:", err);
});
console.log(this.state.users);
}
constructor(props) {
super(props);
this.getUsers();
}
render() {
return (.....)
}
};
export default App;
Here's the result when calling the api from the frontend
Any suggestions would be appreciated
Just learning this topic now. Did you login on React front-end? If you just want to retrieve login user's profile.
Here is my solution for your reference.
Firstly, I tried use simple-JWT authentication to set up react and Django.
(google "JWT authentication Django and React", there are many many teaching materials).
Then to log in on react site, and from a response of Django you can retrieve the logged-in user detail. The response.data is a token, which means you can use a "jwt_decode" to get the information you want including: username, id, email. (may have security issue... refer to If you can decode JWT, how are they secure?, just for learning should be fine).
Your code might look like the following:
axios.post("http://127.0.0.1:8000/token/", {
username: username,
password: password,
})
.then((response) => {
let token = response.data.access;
localStorage.setItem("token", token);
let user_id = jwt_decode(response.data.access).user_id;
...
localStorage.setItem("user_id", user_id);
})
Once you got your user_id in localstorage, you can use it to retrieve all the details, your code might look like:
let id = parseInt(localStorage.getItem("user_id"));
const userDetail = (id) => {
const token = localStorage.getItem("token");
axios
.get(`http://127.0.0.1:8000/users/${id}`, {
headers: { Authorization: token },
})
.then(function (response) {
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});
The response.data includes all information you posted on your Django back-end API.
Hope this helped.

Go Api returning Unauthorized

i am new learner of GoLang+React. So that i started a project to learn. I made a RESTful Api with GoLang. Api Link. i made a login system with that api. And successfully i can login and set user data to sessionStorage. But the problem is when i am trying to logout a user by hit the logout endpoint of api with axios. First time it shows Unauthorized. and second time its showing Network Error.
Here is the Request code:
logout = () => {
const user = JSON.parse(sessionStorage.getItem('userData'));
const token = user.token;
const uid = user.id;
const url = "http://localhost:8000/logout"
axios.post(url,{"user_id":uid},{"Authorization":`Bearer ${token}`}).then((response) => response.json()).then((result) => {
let responseJson = result;
console.log(responseJson);
}).catch((error) => {
console.log(error);
})
}
Note: by client application i can logout successfully. But by axios i cant.
You need to provide auth headers under headers property of Axios config object:
axios.post(
url,
{ user_id: uid },
{ headers: { 'Authorization': `Bearer ${token}` } }
)
Explanation:
This is the axios post method signature, check docs:
axios.post(url[, data[, config]])
And the 3rd parameter config should have this interface:
{
...
baseURL?: string;
headers?: any; // this is for headers
params?: any;
....
}
I fixed that problem with adding this code to my package.json file
"proxy":"http://localhost:your_port"

React Cors issue with ExpressJS

EDIT: I am getting the following error when trying to access my server:
POST http://localhost:3001/user/login 500 (Internal Server Error)
I'm not sure where I'm going wrong for this error to keep occurring:
React:
export default class Account extends React.Component {
constructor() {
super();
this.state = {
isLoggedIn: false,
};
this.login = this.login.bind(this);
}
componentDidMount() {
const cookies = new Cookies();
const user = cookies.get('user');
const pass = cookies.get('pass');
this.setState({processing: true})
fetch('http://localhost:3001/user/login', {
credentials : 'omit',
method : 'POST',
body : JSON.stringify({
username : user,
password : pass
})
})
.then(res => res.json())
.then(json => {
// If the login was successful
if (json.success) {
this.setState ({
isLoggedIn: true
})
}
// Otherwise
else {
this.setState({
errorMessage: 'Invalid username or password',
processing : false
});
}
})
.catch(() => {
this.setState({
errorMessage: 'An unknown error occurred',
processing : false
});
});
}
render() {
if(this.state.isLoggedIn) {
return (<p> Logged In</p>);
}
else {
return (<p> Not Logged In</p>);
}
}}
Express:
router.post('/login', (req, res) => {
return User
.findOne({username: req.body.username})
.then (user => user.authenticate(req.body.password))
.then (auth => {
if (auth.user !== false) {
req.session.user = auth.user.user
}
res.json({success: auth.user !== false})
})
.catch(() => res
.status(500)
.json({success: false})
);
});
This error isn't giving much info on what I could be doing wrong but it could be to do with cors.
There problem is not in your code ( probably ); since a request will be considered as SAME-ORIGIN if domain, port and protocol all be the same in source and destination of the request; but your request is heading to port 3001 not 3000 which violates the same-origin rule; hence CROSS-ORIGIN; two other part are ok, both on localhost and http but port different; you need to configure your server to respond properly to OPTION(pre flight) request to fix this properly;
Have you added a proxy parameter to the package.json? I use this setup frequently but have not seen the issue you are seeing. Try adding the following parameter to the package.json
{
"name": "mern",
"version": "0.1.0",
"proxy": "http://localhost:3001/"
...
}
And then all of your API calls can just be /api because the proxy parameter has been set.
Your console reads: Cannot read property 'authenticate' of null.
In your .catch of api -
.catch(() => res
.status(500) // you are sending 500 (internal server error) irrespective of what your actual error is//
.json({success: false})
);
Your error is -
return User
.findOne({username: req.body.username})
.then (user => user.authenticate(req.body.password)) //can read authenticate of null because user is null i.e This user does not exist in your DB. //
I suggest add err in your catch block as parameter and do proper error handling. This issue is the case of improper error handling.

Using local storage for all subdomains in Reactjs

I have 2 subdomains and 1 main page: product.com, expert.product.com and user.product.com.
After user login in the main page: product.com, my code will check which role of user belongs to: expert, user. If user's role is expert, it will direct user to expert.product.com.
I save user to local storage. I would like to use cross-domain local storage in Reactjs. However, I do not know how to use it :(.
Where should I use cross-domain local storage: in repository of main page, expert.product.com or user.product.com? Do I need to set up from my server or cross-domain local storage can be done in front end side? Could anyone help me for this case?
Set localstorage
handleSubmit = event => {
event.preventDefault();
api
.post(`myapi, {
identifier: this.state.userSignIn.emailSignIn,
password: this.state.userSignIn.passwordSignIn,
})
.then(response => {
localStorage.setItem('userData', response.data.user);
localStorage.setItem('user', JSON.stringify(response.data));
this.setState({ auth: true, open: false, openSignin: false });
this.props.history.push('/profile');
})
.catch(error => {
console.log('An error occurred:', error);
});
};
Get LocalStorage
componentDidMount() {
if (localStorage.getItem('userData')) {
const userObject = JSON.parse(localStorage.getItem('user'));
const jwt = userObject.jwt;
const config = {
headers: { 'Authorization': `bearer ${jwt}` },
};
}

React Microsoft Outlook Calendar data Without Login Session using Hello.js

I am creating a React calendar that take data from "Microsoft Outlook Calendar" using the client-side JavaScript SDK "hello.js" and Microsoft Graph (for the set up I also followed this guide: https://learn.microsoft.com/en-us/graph/auth-register-app-v2).
Using hello.login my app shows the calendar without any problem...but unfortunately I have to show it without a login session.
This is my code:
class CalendarView extends Component {
constructor(props) {
super(props);
hello.init({
microsoft: {
id: APP_ID,
oauth: {
version: 2,
auth: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
},
scope_delim: ' ',
form: false,
scope: SCOPES,
},
});
const { startDate, endDate } = this.props;
this.state = {
// events: [],
startDataTime: startDate.toISOString(),
endDataTime: endDate.toISOString(),
token: hello('microsoft').getAuthResponse().access_token,
};
}
In this other component I mange the Microsoft Graph Query:
class EventsList extends Component {
constructor() {
super();
this.state = {
events: [],
};
}
componentWillReceiveProps(nextProps) {
const { startDate, endDate, token } = nextProps;
// to know what is the Bearer toke
// -> https://stackoverflow.com/questions/25838183/what-is-the-oauth-2-0-bearer-token-exactly
axios.get(
`https://graph.microsoft.com/v1.0/me/calendarview?startdatetime=${startDate}&enddatetime=${endDate}&orderby=start/dateTime`,
{ headers: { Authorization: `Bearer ${token}` } },
).then(response => this.setState({ events: response.data.value }))
.catch((error) => {
console.log(error.response);
});
}
render() {
const { events } = this.state;
if (events !== null) return events.map(event => <EventList key={event.id} event={event} />);
return null;
}
}
The strange thing is that if I make a console.log(token) the app show me the token but, at the same time, I receive an "GET...401 (Unauthorized)" error
console log token and error message
That are my app propriety:
app propriety part 1
app propriety part 2
Maybe the problem is the Hello.js call?
I am testing my app with Jest and I have this error, can it be linked to my problem?
console.error node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/virtual-console.js:29
Error: Uncaught [TypeError: hello is not a function]
How Can I solve?
I found the solution!
I had to make 2 axios call:
one to obtain the token (with a POST)
one for use the token in my microsoft graph query (with a GET)
I had to register my app here https://portal.azure.com/#home so to obtain a Client ID and Secret.
After I needed to send a POST message to Azure Active Directory Authentication endpoint with following body parameters:
grant_type: The flow we want to use, client_credentials in my case.
client_id: The Client ID (Application ID) of the application I
created in the registration step;
client_secret: The Client Secret I created in the registration
step;
resource: The name of the resource I would like to get access,
https://graph.microsoft.com in this case.
So I created one component with the following axios POST request:
componentDidMount() {
axios.post(`https://cors-anywhere.herokuapp.com/https://login.microsoftonline.com/${AZURE_ACTIVE_DIRECTORY_TENANT_NAME}/oauth2/token`,
`grant_type=${GRANT_TYPE}&client_id=${APP_ID}&client_secret=${SECRET}&resource=${RESOURCE}`).then(res => this.setAccessToken(res.data.access_token))
.catch((error) => {
console.error(error.response);
});
}
setAccessToken(token) {
if (typeof token === 'string') this.setState({ accessToken: token });
}
NOTE
the resource value needed to be a bit changed to work:
https%3A%2F%2Fgraph.microsoft.com%2F
I had to put the string 'https://cors-anywhere.herokuapp.com' before micorsoftonline URL because otherwise the application generated
"a blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource."
(I don't know why, I am still working on it because putting this string before is not an optimal solution).
In EventList component I didn't need hellojs anymore, so I just use the token I generated to access. I had to change just a bit the microsoft graph query:
componentDidMount() {
const { accessToken } = this.props;
const { startDate, endDate } = this.state;
this.getEvents(startDate, endDate, accessToken);
}
getEvents(startDate, endDate, accessToken) {
const startDateString = startDate.toISOString();
const endDateString = endDate.toISOString();
axios.get(
`https://graph.microsoft.com/v1.0/users/${USER_PUBLIC_ID}/calendarview?startdatetime=${startDateString}&enddatetime=${endDateString}&orderby=start/dateTime`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
).then(response => this.setEvents(response.data.value))
.catch((error) => {
console.error(error.response);
});
}
setEvents(events) {
const validEvent = event => typeof event.subject === 'string';
this.setState({ events: events.filter(validEvent) });
}
I hope that my solution can be usefull also to other users

Resources