1

I'm building a rest API with auth implementation using Django-rest-framework and Django-rest-knox, And in the front-end I'm using React.js. I want to store the auth token securely in the front-end and I know that the best way to do it is using httponly cookies so I've used this in my code:

django-rest-knox with cookies

from django.contrib.auth import login
from rest_framework import permissions
from rest_framework.authtoken.serializers import AuthTokenSerializer
from knox.views import LoginView as KnoxLoginView


class LoginView(KnoxLoginView):
    permission_classes = (permissions.AllowAny,)

    def post(self, request, format=None):
        serializer = AuthTokenSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data['user']
        login(request, user)
        response = super(LoginView, self).post(request, format=None)

        token = response.data['token']
        del response.data['token']

        response.set_cookie(
            'auth_token',
            token,
            httponly=True,
            samesite='strict'
        )

        return response

This code works perfectly so it send a Set-Cookie header and create an httponly cookie. But the problem is that if those cookies aren't accessible with JavaScript how can I access protected routes from react and axios using the Authorization header.

Fady's Cube
  • 158
  • 1
  • 7

1 Answers1

1
  1. Create a higher order component as "ProtectedRoute" that renders component only if user is authenticated and authorized.

ProtectedRoute Component

const ProtectedRoute = ({component:Component, ...rest}) => {
    const [isLoggedIn, setLoggedIn] = useState({loggedIn:'', loaded:false})
    useEffect(async () => {
        const userStatus = await validateUserLoggedIn();
        setLoggedIn((prevState) => {return {loggedIn:userStatus, loaded:true}})
    }, [])

    return(
        <Route {...rest} render={(routerProps) => {
            if(isLoggedIn.loaded){
                if(isLoggedIn.loggedIn ==='success'){
                    return(
                        <Component {...rest} {...routerProps}/>
                    )
                }else{
                    return(
                        <Redirect to={{
                            pathname:'/login',
                            state:{from : routerProps.location}
                        }} />
                    )
                }
            }else{
                return(
                    <Row style={{display: 'flex', justifyContent: 'center'}}>
                      (<CustomLoader />)
                    </Row>
                )
            }
        }}
        />
    )
}


<ProtectedRoute exact path="/admin" component={Admin} />
  1. ProtectedRoute is a HOC component that returns the component based on "validateUserLoggedIn" return value. Refer "react-router-dom" for more info. Syntax to create HOC for routes is as below

psuedo code

const ProtectedRoute({component:Component, ...rest}) => {
    return (
        <Route {...rest} render={
            (routerProps) => {
                isUserAuthenticated ?
                (<Component {...rest} {...routerProps} />) :
                "redirect to login or display unauthorized"
            }
        }
    )
}
  1. validateUserLoggedIn function makes an API call using the credentials stored in HTTP only cookies and if request is forbidden, it will make a call to an refresh end-point to get new access token and make a request again to verify is user authenticated.

ValidateuserLoggedIn

const validateUserLoggedIn = async () => {
    const loggedIn = {status:''}

    const axiosValidateUserLoggedIn = axios.create(VALIDATEUSERAXIOSCONFIG);

    axiosValidateUserLoggedIn.interceptors.response.use(
        (resp) => {return resp},
        (err) => {
            const originalRequest = err.config;

            const refreshTokenStatus = refreshToken();

            if(refreshTokenStatus === 'success'){
                axios(originalRequest)
            }else{
                window.location.href = '/login';
            }
            // Does this necessary
            return Promise.reject(err)
        }
    )

    await axiosValidateUserLoggedIn.get('userloggedin/')
    .then((resp) => {
        loggedIn.status = resp.data.loggedIn
    })


    console.log(`inside validateUserLoggedIn  - ${loggedIn.status}`)
    return loggedIn.status;
}
  1. I have used dj-rest-auth for token authentication.
B.Anup
  • 585
  • 4
  • 6