I have a project using React (frontend) and Django Rest Framework (backend), and it is currently deployed on PythonAnywhere. I'm using axios to connect to my API and load data from my database onto the React frontend.
During development, I hardcoded the username and password for accessing the database into my index.js
file (credentials are obscured below):
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App.js';
import axios from 'axios';
import 'semantic-ui-css/semantic.min.css';
import 'lightgallery.js/dist/css/lightgallery.css';
import './styles.css';
import * as serviceWorker from './serviceWorker';
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.xsrfCookieName = "csrftoken";
axios.defaults.withCredentials = true;
axios.post('/login/', { username: [HARD_CODED_USERNAME], password: [HARD_CODED_PASSWORD] }).then(rv => {
console.log('Login', rv)
}).catch(err => {
console.log('Login error', err.response)
});
const updatePhoto = () => {
axios.patch('https://[WEBSITEADDRESS.COM]/api/photos/').then(resp => {
console.log('Update response', resp)
}).catch(error => {
console.log("Update error", error)
})
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
serviceWorker.unregister();
This works, however the username and password are viewable upon inspection in the browser. Not only that, but the Django admin as well as the API is accessible to anyone, because it automatically logs them in using my username and password!
I then tried using an .env
file located at the root of my create-react-app project (same level with my package.json
file):
REACT_APP_USERNAME=myusername
REACT_APP_PASSWORD=mypassword
And I updated my index.js
file to as follows:
const my_user_name = process.env.REACT_APP_USERNAME;
const my_password = process.env.REACT_APP_PASSWORD;
axios.post('/login/', { username: my_user_name, password: my_password }).then(rv => {
console.log('Login', rv)
}).catch(err => {
console.log('Login error', err.response)
});
However, this still does not obscure the credentials from inspection in the browser, and, while it does solve the issue of automatically logging anyone into my Django admin and API, the data from the database is not shown on the website.
My questions are as follows:
Any help is much appreciated!
UPDATE: SOLUTION
Based on @Lior_Pollak's comment on his answer below, I managed to solve both of my issues by creating a public, read-only API endpoint on the backend. Anyone can view the API but cannot post, update, or delete data, nor can they access the Django admin. And no sensitive data is displayed in the browser's inspection tool, yet all my photos are displayed on the website. :)
In case anyone else stumbles across this question, I've provided the code I've used successfully below (both backend and frontend):
Frontend: index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App.js';
import 'semantic-ui-css/semantic.min.css';
import 'lightgallery.js/dist/css/lightgallery.css';
import './styles.css';
import * as serviceWorker from './serviceWorker';
/*Removed all code related to axios; was not needed here*/
ReactDOM.render(
<App />,
document.getElementById('root')
);
serviceWorker.unregister();
Backend: views.py
from django.views.generic import View
from django.http import HttpResponse
from django.conf import settings
from rest_framework import generics
from rest_framework import permissions
from .models import Photo
from .serializers import PhotoSerializer
import logging
import os
class PhotoList(generics.ListCreateAPIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
queryset = Photo.objects.filter(show=True).order_by('order')
serializer_class = PhotoSerializer
class FrontendAppView(View):
def get(self, request):
print (os.path.join(settings.REACT_APP_DIR, 'build', 'index.html'))
try:
with open(os.path.join(settings.REACT_APP_DIR, 'build', 'index.html')) as f:
return HttpResponse(f.read())
except FileNotFoundError:
logging.exception('Production build of app not found')
return HttpResponse(status=501)
Backend: urls.py
from django.conf.urls import include, url
from rest_framework import routers
from backend import views
router = routers.DefaultRouter()
urlpatterns = [
url(r'^api/', include(router.urls)),
url(r'^api/photos/$', views.PhotoList.as_view()),
]
You are trying to obscure client data, which (for obvious reasons) resides in the client.
So you can't really obscure it.
You can force the user to login with their credentials, which is how authentication using username and password is done.