Django + ReactJS. The Httponly cookie is not saved in the browser at React side. I also gave {withcredentials : true} at both side - reactjs

I use django rest_framework_simplejwt package to generate JWT tokens and set them in browsable cookie with Httponly flag. At the Django side it work perfectly but at react side it does not work perfectly.
I read many answers related to this question like this and this but they have not solved my problem yet.
Please help me understand where I'm wrong.
DJANGO SIDE
views.py
from rest_framework_simplejwt.views import TokenObtainPairView
from django.conf import settings
from rest_framework import status
from rest_framework_simplejwt.exceptions import TokenError,\
InvalidToken
from rest_framework.response import Response
class MyTokenObtainPairView(TokenObtainPairView):
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
try:
serializer.is_valid(raise_exception=True)
except TokenError as e:
raise InvalidToken(e.args[0])
# set access token in browser with Httponly cookie.
res = Response(serializer.validated_data, status=status.HTTP_200_OK)
access_token = serializer.validated_data['access']
res.set_cookie("access_token", access_token, max_age=settings.SIMPLE_JWT.get('ACCESS_TOKEN_LIFETIME').total_seconds(),samesite='Lax',secure=False, httponly=True)
return res
authentication.py
from rest_framework_simplejwt.authentication import JWTAuthentication
from django.conf import settings
class CookieHandlerJWTAuthentication(JWTAuthentication):
def authenticate(self, request):
# If cookie contains access token, put it inside authorization header
access_token = request.COOKIES.get('access_token')
if(access_token):
request.META['HTTP_AUTHORIZATION'] = '{header_type} {access_token}'.format(
header_type=settings.SIMPLE_JWT['AUTH_HEADER_TYPES'][0], access_token=access_token)
return super().authenticate(request)
urls.py
from .views import MyTokenObtainPairView
urlpatterns = [
......
path('auth/', include('djoser.urls')),
# path('auth/', include('djoser.urls.jwt')),
path('auth/api/token/', MyTokenObtainPairView.as_view(), name='token_obtain_pair'),
]
settings.py
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = (
'http://localhost:3000',
'http://127.0.0.1:8000'
)
Work perfectly(Token set in cookie with Httponly.):
REACTJS SIDE
Login.js
axios.defaults.withCredentials = true
const Login = () => {
const [ivalue, setValue] = useState({});
const { state, dispatch } = useContext(Authcontext);
.......
const loginClick = (e) => {
e.preventDefault();
setLoader(true);
axios.post('http://127.0.0.1:8000/auth/api/token/', {
username: ivalue.username,
password: ivalue.password,
})
.then((res) => {
setValue({ password: "" });
dispatch({
type: LOGIN_SUCCESS,
});
setLoader(false);
history.push("/my_profile");
})
.catch((err) => {
setLoader(false);
setOpen({ act: true, msg: "error" });
dispatch({
type: LOGIN_ERROR,
});
setError(err.response.data);
});
};
.........
};
Myprofile.js
axios.defaults.withCredentials = true
const MyProfile = () => {
const history = useHistory();
const { state, dispatch } = useContext(Authcontext);
useEffect(() => {
const auth_check = () => {
axios.get('http://127.0.0.1:8000/auth/users/me/')
.then((res) => {
dispatch({
type: AFTER_LOGIN,
payload: res.data
});
})
.catch((err) => {
history.push('/login')
});
};
auth_check();
}, []);
}
.......
JWT Response(cookie not set in browser).
Unauthorized Error

I set backend and frontend under same IP. ex. my backend is
localhost:8000
and frontend is
localhost:3000
different ports same Ip.
This is not the scenario when it goes to production you can have any domain.

You are getting a samesite=Lax value in your set-cookie header try using CSRF_COOKIE_SECURE = True, SESSION_COOKIE_SECURE = True, CSRF_COOKIE_SAMESITE = 'None', SESSION_COOKIE_SAMESITE = 'None'
this will let browser to set cookies even from a different domain.
and use withCredentials: true on your client side.

Related

How to wait for a certain axios response before triggering redux-toolkit middleware? - react, socket.io, redux toolkit & express

tl;dr: I want to wait for the axios request response in App.tsx before the const socket = io() initialization in socketMiddleware.ts triggers
The authorization headers are received through an axios request as soon as the client loads the react app.
This axios request triggers as soon as the react app is loaded and refreshes after a certain time.
In App.tsx:
App.tsx
const silentRefresh = useCallback(async () => {
try {
const response: AxiosResponse = await axios.get(
(import.meta.env.VITE_BASEURL as string) + 'auth/refresh-token'
)
axios.defaults.headers.common[
'Authorization'
] = `Bearer ${response.data.token}`
//set user
setTimeout(() => {
silentRefresh()
}, response.data.expiresIn * 1000 - 10000)
} catch (response: any) {
if (response.status !== 201) {
console.log('Not Authorized')
}
}
}, [dispatch])
useEffect(() => {
silentRefresh()
}, [silentRefresh])
This sets the authorization headers (if the client has the httpOnly cookie, to automatically log in) which authorizes the user for protected API endpoints of my express server, and refresh after a certain time.
I want to use this header as authorization token for the socket connection too.
In my redux-toolkit store.ts I added a middleware:
store.ts
const store = configureStore({
reducer: {/*reducers...*/},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(socketMiddleware()),
})
The socketMiddleware.ts looks like this, it tries to set the token from the headers but they are not received at this point:
socketMiddleware.ts
const socketMiddleware = () => {
const socket = io(import.meta.env.VITE_BASEURL as string, {
withCredentials: true,
auth: {
token: axios.defaults.headers.common['Authorization'],
},
})
return (store: any) => (next: any) => (action: any) => {
//some code...
next(action)
}
}
On my server.ts I check for the auth token, but it is undefined as the socket connection gets established before the auth headers are set on the client through the axios request in App.tsx
server.ts
io.use((socket, next) => {
try {
const token = socket.handshake.auth.token
if (!token) {
const error: any = new Error('No token sent, authorization denied')
error.statusCode = 401
next(error)
}
const decoded: any = jwt.verify(token, process.env.REFRESH_SECRET as string)
next()
} catch (error) {
console.log(error)
}
})
io.on('connection', (socket) => {
socket.emit('Hello from Server')
})
Thank you for your help.

How do I persist auth between react and a flask server

I created a flask server and added the following endpoints to this server:
#auth_routes.route('/login', methods=['POST'])
def log_in():
log_in_data = request.get_json()
user = User.query.filter(User.email == log_in_data['email']).first()
if (not any([user])):
return {'message': 'invalid email'}, 401
if not user.check_password(log_in_data['password']):
return {'message': 'invalid password'}, 401
login_user(user)
return {'message': 'success'}
#auth_routes.route('/logged_in')
def logged_in():
return {'logged in': current_user.is_authenticated}
When I hit the /login endpoint and then the /logged_in endpoint via postman, I see that the login has persisted.
but if I do the same thing with react, by using axios it is not persisted.
On the backend I am using flask_login to manage the authentication.
I'm not getting any error messages, however this is not what I am intending. I am trying to get the code to persist the login on the backend - but this only works when I use postman and not react.
This is the function that is invoked on the front-end.
const loginFunc = async () => {
const result = await axios.post('http://127.0.0.1:5000/auth/login', {email, password})
const loggedin = await axios.get('http://127.0.0.1:5000/auth/logged_in')
console.log(result, loggedin)
}
I thought that the flask_login would completely handle the session / authentication, but it doesn't seem to persist it.
Am I missing something? Am I also supposed to send something over with the request when I am using flask_login?
const loginFunc = async () => {
const result = await axios.post('http://127.0.0.1:5000/auth/login', {email, password}) <—- this post request needs {withCredentials: true}
const loggedin = await axios.get('http://127.0.0.1:5000/auth/logged_in') <—- same here
console.log(result, loggedin)
}
Try:
const loginFunc = async () => {
const result = await axios.post('http://127.0.0.1:5000/auth/login' {withCredentials: true}, {email, password})
const loggedin = await axios.get('http://127.0.0.1:5000/auth/logged_in', {withCredentials: true})
console.log(result, loggedin)
}

How to get session in NextJS api route using with-iron-session?

I am using with-iron-session for authentication in my NextJS app however I'm not able to access the session cookie when I make API calls in my getServerSideProps() function. The API route is unable to get the session cookie used for authentication:
Session creation:
// this file is a wrapper with defaults to be used in both API routes and `getServerSideProps` functions
import { withIronSession } from "next-iron-session";
export default function withSession(handler) {
return withIronSession(handler, {
password: process.env.PASSWORD_HASH,
cookieName: "MYCOOKIE",
cookieOptions: {
// the next line allows to use the session in non-https environements like
// Next.js dev mode (http://localhost:3000)
secure: process.env.NODE_ENV === "production",
httpOnly: false,
},
});
}
My getServerSideProps call:
export const getServerSideProps = withSession(async ({ req, res }) => {
const user = req.session.get("user");
if (!user) {
return {
redirect: {
permanent: false,
destination: "/"
},
props: {}
};
}
// I've replaced 'include' with 'same-origin' but it didn't make a difference
const watchRes = await fetch('/watch',{credentials: 'include'});
const watch = await watchRes.json();
return{
props: {
user,
watch
}
}
}
the api route:
// I've added await before the (req,res) but that was just guessing at this point
export default withSession((req, res) => {
const user = req.session.get("user");
if(user){
res.send("Good");
}else{
res.status(403).end();
}
}
While being logged in and I go to my localhost/api/watch route, it shows "Good" but when I try the fetch request I get the 403.
try to add headers of the getServerSideProps request to the second call.
const headers = req.headers
const watchRes = await fetch('/watch',{headers});
in this way you have injected the cookies

How to do Basic Auth with Axios in react

React Code
import React, { useState, useEffect } from "react";
import axios from "axios";
const Table = () => {
useEffect(() => {
console.log("helllllllllllllllo");
callAPI();
}, []);
const callAPI = async () => {
const url = "some URL";
const password = "Secret" ;
const username = "Consumer Key";
const data = await axios.get(
url,
{},
{
auth: {
username: username,
password: password,
},
}
);
console.log("data", data);
};
return (
<div> Hello </div>
);
};
export default Table;
On Postman, I go to the Authorization Tab and input the Username and password in their respective input fields and get the result but with axios, I getting 401 error.
Exact Error being :-
createError.js:16 Uncaught (in promise) Error: Request failed with status code 401
at createError (createError.js:16)
at settle (settle.js:17)
at XMLHttpRequest.handleLoad (xhr.js:62)
You are incorrectly passing the auth headers. In axios GET, first parameter is URL and second parameter is config object. In config object, you can provide auth property for sending basic auth headers.
const data = await axios.get(url, {
auth: {
username: username,
password: password,
},
})

Why does my POST reques result in a 404 error?

I've been struggling to submit a post request from my react app to my Django rest framework api: keep on getting this error:
404 not found Cannot POST /api/tokens/
I am using redux to handle state and post the data to my django api running on localhost:8000:
export const addToken = (company_name, name, address, supply, holders) => {
return dispatch => {
let headers = {"Content-Type": "application/json"};
let body = JSON.stringify({company_name, name, address, supply, holders,});
debugger
return fetch("/api/tokens/", {headers, method: "POST", body})
.then(res => res.json())
.then(token => {
return dispatch({
type: 'ADD_TOKEN',
token
})
})
}
}
In webpack.donf.dev.js:
const publicPath = 'http://localhost:3000/';
const publicUrl = 'http://localhost:3000/';
and here is my url.py:
from contract import endpoints
urlpatterns = [
url(r'^api/', include(endpoints)),
path('admin/', admin.site.urls),
url(r'^api-auth/', include('rest_framework.urls')),
url(r'^', TemplateView.as_view(template_name="index.html")),
]
and the endpoint.py
from contract import views
from .api import TokenViewSet
router = routers.DefaultRouter()
router.register('tokens', TokenViewSet, 'tokens')
urlpatterns = [
url("^", include(router.urls)),
path('tokens/', views.TokenList.as_view()),
path('tokens/<int:pk>/', views.TokenDetail.as_view()),
path('users/', views.UserList.as_view()),
path('users/<int:pk>/', views.UserDetail.as_view()),
]
Not sure if this is enough info as I am pretty new to React.js
UPDATE
Here is my settings.py
WEBPACK_LOADER = {
'DEFAULT': {
'BUNDLE_DIR_NAME': 'bundles/',
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.dev.json'),
}
}
then webpack-stats-dev.json:
{"status":"done","publicPath":"http://localhost:3000/","chunks":{"main":[{"name":"static/js/bundle.js","publicPath":"http://localhost:3000/static/js/bundle.js","path":"/Users/cyrusghazanfar/Desktop/python-projects/smart-contract-distributor/vestvault/web_interface/dist/static/js/bundle.js"},{"name":"main.d66a743648ca15de12df.hot-update.js","publicPath":"http://localhost:3000/main.d66a743648ca15de12df.hot-update.js","path":"/Users/cyrusghazanfar/Desktop/python-projects/smart-contract-distributor/vestvault/web_interface/dist/main.d66a743648ca15de12df.hot-update.js"},{"name":"static/js/bundle.js.map","publicPath":"http://localhost:3000/static/js/bundle.js.map","path":"/Users/cyrusghazanfar/Desktop/python-projects/smart-contract-distributor/vestvault/web_interface/dist/static/js/bundle.js.map"},{"name":"main.d66a743648ca15de12df.hot-update.js.map","publicPath":"http://localhost:3000/main.d66a743648ca15de12df.hot-update.js.map","path":"/Users/cyrusghazanfar/Desktop/python-projects/smart-contract-distributor/vestvault/web_interface/dist/main.d66a743648ca15de12df.hot-update.js.map"}]}}

Resources