Thursday, December 5, 2024
HomePythonEstablishing & Deploying JWT based mostly auth utilizing Flask & React

Establishing & Deploying JWT based mostly auth utilizing Flask & React


Hello everybody! 👋 I used to be engaged on a React undertaking these final couple of weeks and I needed to implement login performance. I searched round and many of the options I discovered relied on Redux or another enormous dependency that I wasn’t already utilizing in my undertaking. I didn’t wish to introduce an enormous dependency so determined to proceed trying. In the long run, I discovered an exquisite library by Oleg Babichev that allowed me to implement the login performance on the front-end pretty simply. The backend for the login was based mostly on Flask and flask-praetorian. flask-praetorian permits us to make use of JWT for auth and handles many of the exhausting logic itself. We simply must outline some endpoints and a few fundamental logic.

On this article, I’m going to take you from a recent React undertaking to a undertaking with a totally practical login backed by flask-praetorian. I hope this text helps you fill all of the data gaps that you simply might need.

Be aware: All of the code from this text could be present in this GitHub repository.

New React undertaking

I’m assuming that you have already got npx put in and have entry to the create-react-app command. Let’s begin by creating a brand new sample-app undertaking:

$ npx create-react-app sample-app

This command will take a while however you’ll find yourself with a sample-app folder with the next construction:

.
├── README.md
├── package deal.json
├── node_modules
│   └── ...
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── src
│   ├── App.css
│   ├── App.js
│   ├── App.check.js
│   ├── index.css
│   ├── index.js
│   ├── emblem.svg
│   ├── serviceWorker.js
│   └── setupTests.js
└── yarn.lock

Now let’s begin the event server for our React app:

$ yarn begin

Candy! It’s best to see one thing just like this:

React Default

Establishing Flask API

Now that we’ve got the essential React app working, it’s time to arrange the Flask undertaking that’s going to function our API backend. We are going to create an api folder contained in the sample-app folder and that is the place all of our Flask (Python) code goes to be saved. Whereas we’re at it, we will even arrange a digital setting contained in the api folder and activate it.

$ mkdir api
$ cd api
$ contact api.py
$ python3 -m venv env
$ supply env/bin/activate

Our folder construction will look one thing like this now:

.
├── README.md
├── package deal.json
├── node_modules
│   └── ...
├── api
│   ├── env
|   │   └── ...
│   └── api.py
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── src
│   ├── App.css
│   ├── App.js
│   ├── App.check.js
│   ├── index.css
│   ├── index.js
│   ├── emblem.svg
│   ├── serviceWorker.js
│   └── setupTests.js
└── yarn.lock

Now let’s set up the Python libraries that we are going to be utilizing:

$ pip set up flask
$ pip set up flask_sqlalchemy
$ pip set up flask-praetorian
$ pip set up flask_cors

We’re prepared to write down our Python code! Open up your favourite textual content editor and paste the next code within the api.py file we created earlier:

import os
import flask
import flask_sqlalchemy
import flask_praetorian
import flask_cors

db = flask_sqlalchemy.SQLAlchemy()
guard = flask_praetorian.Praetorian()
cors = flask_cors.CORS()


# A generic person mannequin that is perhaps utilized by an app powered by flask-praetorian
class Consumer(db.Mannequin):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.Textual content, distinctive=True)
    password = db.Column(db.Textual content)
    roles = db.Column(db.Textual content)
    is_active = db.Column(db.Boolean, default=True, server_default="true")

    @property
    def rolenames(self):
        attempt:
            return self.roles.break up(',')
        besides Exception:
            return []

    @classmethod
    def lookup(cls, username):
        return cls.question.filter_by(username=username).one_or_none()

    @classmethod
    def determine(cls, id):
        return cls.question.get(id)

    @property
    def identification(self):
        return self.id

    def is_valid(self):
        return self.is_active


# Initialize flask app for the instance
app = flask.Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = 'high secret'
app.config['JWT_ACCESS_LIFESPAN'] = {'hours': 24}
app.config['JWT_REFRESH_LIFESPAN'] = {'days': 30}

# Initialize the flask-praetorian occasion for the app
guard.init_app(app, Consumer)

# Initialize an area database for the instance
app.config['SQLALCHEMY_DATABASE_URI'] = f"sqlite:///{os.path.be a part of(os.getcwd(), 'database.db')}"
db.init_app(app)

# Initializes CORS in order that the api_tool can speak to the instance app
cors.init_app(app)

# Add customers for the instance
with app.app_context():
    db.create_all()
    if db.session.question(Consumer).filter_by(username="Yasoob").rely() < 1:
        db.session.add(Consumer(
          username="Yasoob",
          password=guard.hash_password('strongpassword'),
          roles="admin"
            ))
    db.session.commit()


# Arrange some routes for the instance
@app.route('/api/')
def residence():
    return {"Hey": "World"}, 200

  
@app.route('/api/login', strategies=['POST'])
def login():
    """
    Logs a person in by parsing a POST request containing person credentials and
    issuing a JWT token.
    .. instance::
       $ curl http://localhost:5000/api/login -X POST 
         -d '{"username":"Yasoob","password":"strongpassword"}'
    """
    req = flask.request.get_json(drive=True)
    username = req.get('username', None)
    password = req.get('password', None)
    person = guard.authenticate(username, password)
    ret = {'access_token': guard.encode_jwt_token(person)}
    return ret, 200

  
@app.route('/api/refresh', strategies=['POST'])
def refresh():
    """
    Refreshes an current JWT by creating a brand new one that may be a copy of the previous
    besides that it has a refrehsed entry expiration.
    .. instance::
       $ curl http://localhost:5000/api/refresh -X GET 
         -H "Authorization: Bearer <your_token>"
    """
    print("refresh request")
    old_token = request.get_data()
    new_token = guard.refresh_jwt_token(old_token)
    ret = {'access_token': new_token}
    return ret, 200
  
  
@app.route('/api/protected')
@flask_praetorian.auth_required
def protected():
    """
    A protected endpoint. The auth_required decorator would require a header
    containing a legitimate JWT
    .. instance::
       $ curl http://localhost:5000/api/protected -X GET 
         -H "Authorization: Bearer <your_token>"
    """
    return {'message': f'protected endpoint (allowed person {flask_praetorian.current_user().username})'}


# Run the instance
if __name__ == '__main__':
    app.run(host="0.0.0.0", port=5000)

The code is fairly easy. We begin by importing the libraries that we are going to be utilizing after which create the required db, guard, and cors objects. Most of this code is taken from the flask-praetorian web site and I’ve added or eliminated some stuff to make it easier.

On the primary run, the code will generate the database tables and create a brand new person named Yasoob if it doesn’t exist already. Then we’ve got a login route that may give us our JWT tokens upon submitting legitimate credentials and a protected route that’s solely accessible with legitimate tokens. We even have a refresh route that may refresh our entry tokens. Should you don’t understand how JWT tokens work, learn another article as I received’t be discussing that right here.

One necessary factor to notice is that I prefixed all routes with the phrase api. That is going to be essential as we attempt to arrange React to work properly with our API. Our reverse proxy (NGINX) will route all /api requests to our Python API and can route all different requests to the React index.html web site. We are going to speak extra about this later.

At this level we are able to go forward and run our Python API:

$ export FLASK_APP=api.py
$ flask run
 * Working on http://127.0.0.1:5000/

If all the things goes properly, it’s best to be capable to entry http://127.0.0.1:5000/api and see {"hiya": "world"} in return. The flask run command will even generate the database.db file for us.

Simply to be sure that the login endpoint is working accurately, open up a brand new terminal tab/window and run this command:

$ curl http://localhost:5000/api/login -X POST 
         -d '{"username":"Yasoob","password":"strongpassword"}'

It’s best to see one thing just like this:

{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MDI2Mjk5MzcsImV4cCI6MTYwMjcxNjMzNywianRpIjoiYmU3OGVmZTgtZTg0YS00NDljLTllMmUtMmZkMWU0NzliNGZjIiwiaWQiOjEsInJscyI6ImFkbWluIiwicmZfZXhwIjoxNjA1MjIxOTM3fQ.hRrPwiCKQ2jW0bxhgf_qSAEV2B9yXxdswJAdpisTxlc"}

Should you get an error, be sure you pasted the command accurately, and the username and password match with the one we created early on.

We are able to additionally go forward and check the /api/protected endpoint as properly (change <your_token> with the access_token from the earlier output):

$ curl http://localhost:5000/api/protected -X GET 
         -H "Authorization: Bearer <your_token>"

If all the things goes properly, it’s best to see this:

{"message":"protected endpoint (allowed person Yasoob)"}

Should you don’t copy the token accurately, you may see this error (there generally is a completely different error as properly relying on which a part of the token you copied incorrectly):

{"error":"InvalidTokenHeader","message":"did not decode JWT token -- InvalidSignatureError: Signature verification failed","status_code":401}

Candy! Our API is working and we’re able to go to the subsequent step.

Making React work properly with Flask

In a manufacturing setting, you’ll wish to compile your React app right into a bunch of static HTML, CSS, and JS information with different static property (photographs, and so on.) and serve them utilizing one thing like NGINX. NGINX supplies superior efficiency for serving static information. However throughout improvement, I simply use the default React server. We must make some modifications to make it work properly with our backend API although. That is what we finally need to have the ability to do:

  1. Inform React server to proxy all unknown URLs to our Flask occasion
  2. Use yarn to run our Flask API as properly (not mandatory however a pleasant to have)

We are going to work in reverse. The very very first thing we’ll do is create a .flaskenv file in our api folder with the next contents:

FLASK_APP=api.py

This manner we received’t must set the FLASK_APP setting variable earlier than operating flask run. Flask will detect this file and routinely set the variable. However for this to work, we must set up one other Python package deal as properly:

$ pip set up python-dotenv

Now we’ll add a brand new yarn command. For that, edit the package deal.json file in your sample-app listing and replace the scripts part to look one thing like this:

"scripts": {
    "start-api": "cd api && env/bin/flask run --no-debugger",
  "begin": "react-scripts begin",
  "construct": "react-scripts construct",
  "check": "react-scripts check",
  "eject": "react-scripts eject"
}

I realized this trick from Miguel Grinberg. The best way we’ve got set it up, we received’t must activate our digital setting and Flask will routinely load the required packages and libraries from the env folder.

Let’s additionally add this line on the finish of our package deal.json file:

  "proxy": "http://localhost:5000"

This can proxy all unknown incoming requests to localhost:5000 the place our API server is operating. That is just for the event and can enable us to make use of fetch with out worrying about CORS and ports. Now we are able to merely do that on the front-end to ship a request to our Python backend:

fetch("/api")

The complete package deal.json file will look one thing like this:

{
  "title": "sample-app",
  "model": "0.1.0",
  "personal": true,
  "dependencies": {
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.3.2",
    "@testing-library/user-event": "^7.1.2",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-router-dom": "^5.2.0",
    "react-scripts": "3.4.3"
  },
  "scripts": {
    "start-api": "cd api && env/bin/flask run --no-debugger",
    "begin": "react-scripts begin",
    "construct": "react-scripts construct",
    "check": "react-scripts check",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "manufacturing": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "improvement": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "proxy": "http://localhost:5000"
}

Let’s try it out. Go to the sample-app folder and check out operating flask utilizing yarn:

$ yarn start-api

If we haven’t managed to interrupt something, this could present up within the terminal:

yarn run v1.22.5
$ cd api && env/bin/flask run --no-debugger
 * Serving Flask app "api.py"
 * Atmosphere: manufacturing
   WARNING: This can be a improvement server. Don't use it in a manufacturing deployment.
   Use a manufacturing WSGI server as an alternative.
 * Debug mode: off
 * Working on http://127.0.0.1:5000/ (Press CTRL+C to give up)

We is not going to be accessing our app immediately from port 5000. We can be accessing it from 3000. Should you haven’t stopped the yarn begin course of, it’s best to be capable to entry the online app at http://localhost:3000.

Fleshing out React app

Let’s give attention to the front-end for a bit. We can be utilizing react-router-dom for the routing and earlier than we are able to use it, we must set up it:

$ npm set up react-router-dom

Now change all the things in src/App.js with this:

import React, { useEffect } from "react";
import {
  BrowserRouter as Router,
  Swap,
  Route,
  Hyperlink
} from "react-router-dom";

export default perform App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Hyperlink to="/">Residence</Hyperlink>
            </li>
            <li>
              <Hyperlink to="/login">Login</Hyperlink>
            </li>
            <li>
              <Hyperlink to="/secret">Secret</Hyperlink>
            </li>
          </ul>
        </nav>

        {/* A <Swap> appears to be like by its youngsters <Route>s and
            renders the primary one which matches the present URL. */}
        <Swap>
          <Route path="/login">
            <Login />
          </Route>
          <Route path="/secret">
            <Secret />
          </Route>
          <Route path="/">
            <Residence />
          </Route>
        </Swap>
      </div>
    </Router>
  );
}

perform Residence() {
  useEffect(() => {
    fetch("/api").then(resp => resp.json()).then(resp => console.log(resp))
  }, [])
  return <h2>Residence</h2>;
}

perform Login() {
  return <h2>Login</h2>;
}

perform Secret() {
  return <h2>Secret</h2>;
}

This can be a easy app utilizing react-router-dom to show three completely different pages. You possibly can click on on every hyperlink to go to a distinct web page. I’ve additionally added the fetch name to /api simply to substantiate that our React improvement server is accurately proxying requests to our backend. To check it, open up localhost:3000 and open the developer instruments. It’s best to be capable to see one thing just like this:

{ Hey: "World" }

Good! Now we are able to go forward and create a login web page. Modify the Login element like this:

perform Login() {
  const [username, setUsername] = useState('')
  const [password, setPassword] = useState('')

  const onSubmitClick = (e)=>{
    e.preventDefault()
    console.log("You pressed login")
    let opts = {
      'username': username,
      'password': password
    }
    console.log(opts)
    fetch('/api/login', {
      methodology: 'publish',
      physique: JSON.stringify(opts)
    }).then(r => r.json())
      .then(token => {
        if (token.access_token){
          console.log(token)          
        }
        else {
          console.log("Please sort in right username/password")
        }
      })
  }

  const handleUsernameChange = (e) => {
    setUsername(e.goal.worth)
  }

  const handlePasswordChange = (e) => {
    setPassword(e.goal.worth)
  }

  return (
    <div>
      <h2>Login</h2>
      <kind motion="#">
        <div>
          <enter sort="textual content" 
            placeholder="Username" 
            onChange={handleUsernameChange}
            worth={username} 
          />
        </div>
        <div>
          <enter
            sort="password"
            placeholder="Password"
            onChange={handlePasswordChange}
            worth={password}
          />
        </div>
        <button onClick={onSubmitClick} sort="submit">
          Login Now
        </button>
      </kind>
    </div>
  )
}

Additionally, modify the react import on the high of the file and import useState as properly:

import React, { useEffect, useState } from "react";

There are a few issues we’re doing right here. We create two enter parts, one for username and one for the password. We flip these into managed elements and ensure we’re catching the occasion fired by the Login button. The UI ought to refresh routinely and you can be greeted by an unsightly trying however practical kind:

Login Form

Should you enter the username: “Yasoob” and password: “strongpassword”, and click on “Login Now”, it’s best to be capable to see this within the developer instruments console:

Dev tools login output

Looks like we’ve got a fundamental login working!

Persisting login information

We’d like to have the ability to persist these entry tokens and use these to make authenticated requests to our backend. How can we try this? Should you search on-line, you’ll come throughout a number of options utilizing Axios and Redux however we aren’t utilizing both in our undertaking. I don’t wish to use these. The best resolution that involves thoughts is to place the access_token within the native storage and verify for its existence earlier than making any authenticated requests utilizing fetch. If it exists, add it’s worth within the Authorization header.

Fortunately, there’s a library on the market that does precisely that. It’s known as react-token-auth. It has a brilliant easy API and get’s out of your approach. Let’s go forward and set up it after which I’ll present you learn how to use it:

$ npm set up react-token-auth

Now we have to create an occasion of authProvider that comes as part of react-token-auth. I’ll create that within the src/auth/index.js file:

import {createAuthProvider} from 'react-token-auth';


export const [useAuth, authFetch, login, logout] =
    createAuthProvider({
        accessTokenKey: 'access_token',
        onUpdateToken: (token) => fetch('/api/refresh', {
            methodology: 'POST',
            physique: token.access_token
        })
        .then(r => r.json())
    });

Now within the App.js file, add the next import on the high:

import {login} from "./auth"

and modify the fetch command like this:

fetch('/api/login', {
  methodology: 'publish',
  physique: JSON.stringify(opts)
}).then(r => r.json())
  .then(token => {
    if (token.access_token){
      login(token)
      console.log(token)          
    }
    else {
      console.log("Please sort in right username/password")
    }
  })

Should you attempt logging in, nothing could be any completely different from earlier than however now your entry tokens are being saved within the native storage and are accessible in different elements through a hook supplied by the very useful react-token-auth library. We additionally inform react-token-auth concerning the /api/refresh endpoint that it could actually use to refresh our tokens. The best way we’ve got arrange our backend, the entry tokens stay legitimate for a day and have to be refreshed after that through the /api/refresh endpoint. The react-token-auth library will do that token refresh automagically for us. The library additionally supplies us with the authFetch perform that may add the Authorization header routinely for us whether it is saved in native storage.

Let’s modify the Login element a bit extra and add a logout button if the person is already logged in. For that we first must replace the import from ./auth like this:

import {login, useAuth, logout} from "./auth"

Subsequent add this to the login perform someplace earlier than the return assertion:

  const [logged] = useAuth();

And lastly, replace the return like this:

return (
  <div>
    <h2>Login</h2>
    {!logged? <kind motion="#">
      <div>
        <enter sort="textual content" 
          placeholder="Username" 
          onChange={handleUsernameChange}
          worth={username} 
        />
      </div>
      <div>
        <enter
          sort="password"
          placeholder="Password"
          onChange={handlePasswordChange}
          worth={password}
        />
      </div>
      <button onClick={onSubmitClick} sort="submit">
        Login Now
      </button>
    </kind>
    : <button onClick={() => logout()}>Logout</button>}
  </div>
)

The useAuth() hook will return true if there are any entry tokens saved within the native storage. We use that truth to point out the logout button if the person is logged in and the login kind if the person is logged out. The logout button merely calls the logout() perform that removes the entry tokens from storage.

Did you see how a lot this single library made issues a lot simpler?

Making an authenticated name

Let’s go forward and attempt to use the newly saved tokens to create an authenticated name to the protected API endpoint. We can be modifying the Secret element for that:

perform Secret() {
  const [message, setMessage] = useState('')

  useEffect(() => {
    authFetch("/api/protected").then(response => {
      if (response.standing === 401){
        setMessage("Sorry you are not approved!")
        return null
      }
      return response.json()
    }).then(response => {
      if (response && response.message){
        setMessage(response.message)
      }
    })
  }, [])
  return (
    <h2>Secret: {message}</h2>
  )
}

Be sure you additionally add authFetch within the import from ./auth on the high of App.js:

import {login, authFetch, useAuth, logout} from "./auth"

Within the secret endpoint, we’re utilizing authFetch as an alternative of fetch to routinely insert the Authorization header to the request. authFetch is solely a wrapper on high of fetch and has the identical API. We verify the response and if it’s a 401 we show the unauthorized message and if it’s a success, we show the message we obtain from the server.

Strive navigating to the /secret web page and see how the output adjustments relying on if you’re logged in or not. It’s best to see one thing like this:

Unauthorized

Authorized

Fairly neat, eh?

Redirect to login web page

If the person tried to go to the key web page with out truly having logged in we might wish to redirect them to the login web page. Consider what occurs once you attempt to entry Gmail. In case you are logged-in solely then you might be taken to your inbox. In any other case, you might be redirected to the login web page. There are a number of methods to try this. We are able to both verify for the logged worth and redirect based mostly on that or we are able to use Greater-Order Parts. I’ll present you learn how to use HOC.

A HOC is solely a element that takes a element as enter, does some computation and returns a element. We are going to outline a PrivateRoute element that may verify if the person is logged in or not and redirect them to /login in the event that they aren’t logged in:

const PrivateRoute = ({ element: Element, ...relaxation }) => {
  const [logged] = useAuth();

  return <Route {...relaxation} render={(props) => (
    logged
      ? <Element {...props} />
      : <Redirect to='/login' />
  )} />
}

We additionally want to change our imports and import Redirect from react-router-dom:

import {
  BrowserRouter as Router,
  Swap,
  Route,
  Redirect,
  Hyperlink
} from "react-router-dom"

Now the one factor left to do is to swap our /secret Route with the PrivateRoute. We are going to change this code:

<Route path="/secret">
    <Secret />
</Route>

with this:

<PrivateRoute path="/secret" element={Secret} />

Now attempt navigating to /secret and if nothing is damaged you need to be redirected to /login if you’re not at the moment logged in. Candy! That is all I had deliberate for the login stuff. You possibly can take a look at the docs for flask_praetorian to determine learn how to implement registration and all the opposite stuff (registration emails, password reset, and so on.) that goes with it.

Deploying our app utilizing NGINX & Gunicorn

Let’s use NGINX to deploy our app. I’m assuming that you’re operating Ubuntu 20.04. There are a number of methods to do it so if you happen to don’t like this method you may observe a distinct one. We’re aiming for an structure just like this:

Server architecture

The very very first thing we’d like is to construct our react undertaking right into a bunch of static HTML, CSS, and JS information (+ different property). This may be performed by going to the sample-app folder and operating:

$ yarn construct

This could lead to a construct listing being generated within the sample-app folder. We are going to inform NGINX to serve all requests coming to the / endpoint from this construct listing.

We can also’t use the default improvement server of Flask to serve our API. It isn’t as strong as a production-ready server. We can be utilizing Gunicorn as an alternative. Gunicorn will deal with all requests coming to the /api endpoint. And to restart Gunicorn on system restarts and different unintentional points, we’ll use systemd to run Gunicorn as a service. To perform all this, we’ll first set up Gunicorn utilizing PIP:

$ pip set up gunicorn

Subsequent, we have to create a .service file for systemd. I named mine sample-app.service:

[Unit]
Description=Our Pattern React app
After=community.goal

[Service]
Consumer=yasoob
WorkingDirectory=/residence/yasoob/sample-app/api
ExecStart=/residence/yasoob/sample-app/api/env/bin/gunicorn -b 127.0.0.1:5000 api:app --log-file /residence/yasoob/sample-app/api/gunicorn.log --log-level=debug
Restart=all the time

[Install]
WantedBy=multi-user.goal

Be aware: Be sure you replace the Consumer worth to replicate your person account title on the server. Additionally, modify the listing paths as applicable.

Be sure you have stopped the Flask server. Now we are able to transfer sample-app.service to /and so on/systemd/system/sample-app.service and run:

$ systemctl daemon-reload

If issues go properly, gunicorn ought to begin serving our API on port 5000. You possibly can all the time begin/cease/restart the service through the use of the next instructions:

$ sudo service sample-app cease
$ sudo service sample-app begin
$ sudo service sample-app restart

The subsequent step is to delete the create an NGINX configuration file and inform NGINX to serve our app. All of the configurations for various web sites are saved within the /and so on/nginx/sites-available/ listing. That is the place you may create new configurations however NGINX is not going to load them. NGINX will solely load the configurations accessible in /and so on/nginx/sites-enabled/ and the overall observe is to create a symlink in sites-enabled to the completely different information in sites-available. By default, NGINX serves a welcome web page. We don’t need that. Due to this fact, we’ll take away the default file from NGINX. We’re solely eradicating the symlink. This doesn’t take away the default file from sites-available:

$ sudo rm /and so on/nginx/sites-enabled/default

Our tremendous easy NGINX configuration file will appear to be this:

server {
    hear 80;
    root /residence/yasoob/sample-app/construct;
    index index.html;

    location / {
    try_files $uri /index.html;
        add_header Cache-Management "no-cache";
    }

    location /static {
        expires 1y;
        add_header Cache-Management "public";
    }

    location /api {
        embody proxy_params;
        proxy_pass http://localhost:5000;
    }
}

Save this file as sample-app.nginx and transfer it to /and so on/nginx/sites-available. Afterwards, create a symlink to this file within the /and so on/nginx/sites-enabled folder:

sudo ln -s /and so on/nginx/sites-available/sample-app.nginx /and so on/nginx/sites-enabled/sample-app.nginx

And now we are able to reload NGINX in order that this new configuration is loaded:

$ sudo systemctl reload nginx

Now if you happen to go to localhost:80, it’s best to see the React software and the login and signup ought to nonetheless work. Our internet app is prepared.

As soon as issues are working and you might be able to deploy it, you may wish to modify your configuration like this:

server {
    server_name instance.com;
    root /residence/yasoob/sample-app/construct;
    index index.html;

    location / {
    try_files $uri /index.html;
        add_header Cache-Management "no-cache";
    }

    location /static {
        expires 1y;
        add_header Cache-Management "public";
    }

    location /api {
        embody proxy_params;
        proxy_pass http://localhost:5000;
    }
}

server {
    server_name www.instance.com;
    return 301 https://instance.com$request_uri;
}

Substitute server_name with the precise tackle that can be serving this app. I added a 301 redirect from www.instance.com to instance.com. The final step is to generate SSL certificates in order that we are able to serve our web site utilizing HTTPS quite than HTTP. We are able to set up certbot utilizing the official directions and use that to put in the SSL certificates routinely and likewise implement a 301 redirect from HTTP to HTTPS.

After putting in certbot, simply run it utilizing sudo and observe the on-screen directions:

yasoob@server:~/sample-app$ sudo certbot
[sudo] password for yasoob:
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins chosen: Authenticator nginx, Installer nginx

Which names would you wish to activate HTTPS for?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: instance.com
2: www.instance.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Choose the suitable numbers separated by commas and/or areas, or depart enter
clean to pick out all choices proven (Enter 'c' to cancel):

I put in the certificates for each of those and likewise added 301 redirects. Strive going to instance.com (change this together with your internet tackle) and it’s best to be capable to see the shiny padlock proper earlier than the URL. Looks like we have been capable of efficiently get HTTPS to work!

Safety Points

There are a bunch of issues I skipped on the API facet. Our API is open to all kinds of assaults proper now and could be abused by anybody. There isn’t any charge limiting on the login endpoint. Be sure you observe correct safety practices earlier than placing one thing like this in manufacturing. Or if you happen to desire, use a third-party service for person authentication and authorization so that you simply don’t have to fret about many of the safety stuff.

Conclusion

That is all that I had deliberate for this publish. You possibly can take a look at Miguel’s weblog for extra enjoyable stuff about React and Flask. He’s an authority in the case of Flask stuff and his React + Flask deployment articles have been an enormous assist to many (together with me). If you wish to dive deeper into how react-token-auth works, give this text a learn. It’s written by the writer of the package deal.

I attempted to maintain all of the React and Python code in single information in order that it’s simpler to write down an article about them. With that being mentioned, I’m nonetheless a newbie in the case of front-end improvement utilizing React so if you happen to see any evident points on this article please let me know within the feedback beneath. I’ll be sure to repair these points.

Be aware: All of the code from this text could be present in this GitHub repository.

Have an exquisite day everybody! đź‘‹ I’ll see you within the subsequent article ❤️

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments