Tuesday, March 14, 2023
HomePythonOrganising & Deploying JWT primarily based auth utilizing Flask & React

Organising & Deploying JWT primarily based auth utilizing Flask & React


Hello everybody! 👋 I used to be engaged on a React venture these final couple of weeks and I needed to implement login performance. I searched round and a lot of the options I discovered relied on Redux or another enormous dependency that I wasn’t already utilizing in my venture. I didn’t need to introduce a giant 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 primarily based on Flask and flask-praetorian. flask-praetorian permits us to make use of JWT for auth and handles a lot of the arduous logic itself. We simply have to outline some endpoints and a few fundamental logic.

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

Word: All of the code from this text will be present in this GitHub repository.

New React venture

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 venture:

$ 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
├── bundle.json
├── node_modules
│   └── ...
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── src
│   ├── App.css
│   ├── App.js
│   ├── App.take a look at.js
│   ├── index.css
│   ├── index.js
│   ├── brand.svg
│   ├── serviceWorker.js
│   └── setupTests.js
└── yarn.lock

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

$ yarn begin

Candy! You need to see one thing just like this:

React Default

Organising Flask API

Now that now we have the essential React app working, it’s time to arrange the Flask venture 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 can even arrange a digital surroundings 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
├── bundle.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.take a look at.js
│   ├── index.css
│   ├── index.js
│   ├── brand.svg
│   ├── serviceWorker.js
│   └── setupTests.js
└── yarn.lock

Now let’s set up the Python libraries that we’ll 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 may be 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):
        strive:
            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 establish(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 a neighborhood database for the instance
app.config['SQLALCHEMY_DATABASE_URI'] = f"sqlite:///{os.path.be part of(os.getcwd(), 'database.db')}"
db.init_app(app)

# Initializes CORS in order that the api_tool can discuss 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").depend() < 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 house():
    return {"Hi there": "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 present 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 sound 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’ll 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 now we have 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. If you happen to don’t know the way JWT tokens work, learn another article as I received’t be discussing that right here.

One essential factor to notice is that I prefixed all routes with the phrase api. That is going to be crucial 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 discuss 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 effectively, it’s best to have the ability to entry http://127.0.0.1:5000/api and see {"whats up": "world"} in return. The flask run command can even generate the database.db file for us.

Simply to guarantee that the login endpoint is working appropriately, 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"}'

You need to see one thing just like this:

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

If you happen to get an error, ensure you pasted the command appropriately, and the username and password match with the one we created early on.

We will additionally go forward and take a look at the /api/protected endpoint as effectively (exchange <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 effectively, it’s best to see this:

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

If you happen to don’t copy the token appropriately, you may see this error (there is usually a totally different error as effectively 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 following step.

Making React work properly with Flask

In a manufacturing surroundings, you’ll need to compile your React app right into a bunch of static HTML, CSS, and JS recordsdata with different static property (photographs, and so forth.) and serve them utilizing one thing like NGINX. NGINX supplies superior efficiency for serving static recordsdata. 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 ultimately 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 effectively (not vital 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 fashion we received’t must set the FLASK_APP surroundings variable earlier than working flask run. Flask will detect this file and robotically set the variable. However for this to work, we must set up one other Python bundle as effectively:

$ pip set up python-dotenv

Now we’ll add a brand new yarn command. For that, edit the bundle.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",
  "take a look at": "react-scripts take a look at",
  "eject": "react-scripts eject"
}

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

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

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

It will proxy all unknown incoming requests to localhost:5000 the place our API server is working. That is just for the event and can permit 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 bundle.json file will look one thing like this:

{
  "title": "sample-app",
  "model": "0.1.0",
  "non-public": 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",
    "take a look at": "react-scripts take a look at",
    "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 working 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"
 * Surroundings: manufacturing
   WARNING: It is 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 stop)

We won’t be accessing our app immediately from port 5000. We can be accessing it from 3000. If you happen to haven’t stopped the yarn begin course of, it’s best to have the ability 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 exchange all the things in src/App.js with this:

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

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

        {/* A <Change> seems to be by its kids <Route>s and
            renders the primary one which matches the present URL. */}
        <Change>
          <Route path="/login">
            <Login />
          </Route>
          <Route path="/secret">
            <Secret />
          </Route>
          <Route path="/">
            <Dwelling />
          </Route>
        </Change>
      </div>
    </Router>
  );
}

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

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

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

It is a easy app utilizing react-router-dom to show three totally different pages. You’ll be able to click on on every hyperlink to go to a special web page. I’ve additionally added the fetch name to /api simply to substantiate that our React improvement server is appropriately proxying requests to our backend. To check it, open up localhost:3000 and open the developer instruments. You need to have the ability to see one thing just like this:

{ Hi there: "World" }

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

operate 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: 'submit',
      physique: JSON.stringify(opts)
    }).then(r => r.json())
      .then(token => {
        if (token.access_token){
          console.log(token)          
        }
        else {
          console.log("Please sort in appropriate 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 effectively:

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 parts and ensure we’re catching the occasion fired by the Login button. The UI ought to refresh robotically and you’ll be greeted by an unsightly trying however useful kind:

Login Form

If you happen to enter the username: “Yasoob” and password: “strongpassword”, and click on “Login Now”, it’s best to have the ability to see this within the developer instruments console:

Dev tools login output

Looks like now we have a fundamental login working!

Persisting login data

We want to have the ability to persist these entry tokens and use these to make authenticated requests to our backend. How can we do this? If you happen to search on-line, you’ll come throughout a number of options utilizing Axios and Redux however we aren’t utilizing both in our venture. I don’t need to use these. The only answer 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 referred to as react-token-auth. It has a brilliant easy API and get’s out of your means. Let’s go forward and set up it after which I’ll present you tips on 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: 'submit',
  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 appropriate username/password")
    }
  })

If you happen to strive logging in, nothing could be any totally different from earlier than however now your entry tokens are being saved within the native storage and are accessible in different parts by way of a hook supplied by the very helpful react-token-auth library. We additionally inform react-token-auth concerning the /api/refresh endpoint that it might probably use to refresh our tokens. The best way now we have arrange our backend, the entry tokens stay legitimate for a day and must be refreshed after that by way of 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 operate that may add the Authorization header robotically 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 have to replace the import from ./auth like this:

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

Subsequent add this to the login operate 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 indicate 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() operate 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:

operate 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 to 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 robotically insert the Authorization header to the request. authFetch is just 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.

Attempt navigating to the /secret web page and see how the output modifications relying on in case you are logged in or not. You need 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 really having logged in we’d need to redirect them to the login web page. Consider what occurs if you attempt to entry Gmail. If you’re 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 will both verify for the logged worth and redirect primarily based on that or we are able to use Increased-Order Parts. I’ll present you tips on how to use HOC.

A HOC is just 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,
  Change,
  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 exchange this code:

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

with this:

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

Now strive navigating to /secret and if nothing is damaged you need to be redirected to /login in case you are not presently logged in. Candy! That is all I had deliberate for the login stuff. You’ll be able to take a look at the docs for flask_praetorian to determine tips on how to implement registration and all the opposite stuff (registration emails, password reset, and so forth.) that goes with it.

Deploying our app utilizing NGINX & Gunicorn

Let’s use NGINX to deploy our app. I’m assuming that you’re working Ubuntu 20.04. There are a number of methods to do it so in the event you don’t like this method you possibly can observe a special 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 venture right into a bunch of static HTML, CSS, and JS recordsdata (+ different property). This may be executed by going to the sample-app folder and working:

$ yarn construct

This could end in 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’s not 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=/house/yasoob/sample-app/api
ExecStart=/house/yasoob/sample-app/api/env/bin/gunicorn -b 127.0.0.1:5000 api:app --log-file /house/yasoob/sample-app/api/gunicorn.log --log-level=debug
Restart=all the time

[Install]
WantedBy=multi-user.goal

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

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

$ systemctl daemon-reload

If issues go effectively, gunicorn ought to begin serving our API on port 5000. You’ll be able to 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 forth/nginx/sites-available/ listing. That is the place you possibly can create new configurations however NGINX won’t load them. NGINX will solely load the configurations obtainable in /and so forth/nginx/sites-enabled/ and the overall observe is to create a symlink in sites-enabled to the totally different recordsdata 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 forth/nginx/sites-enabled/default

Our tremendous easy NGINX configuration file will appear like this:

server {
    hear 80;
    root /house/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 {
        embrace proxy_params;
        proxy_pass http://localhost:5000;
    }
}

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

sudo ln -s /and so forth/nginx/sites-available/sample-app.nginx /and so forth/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 in the event you 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 need to modify your configuration like this:

server {
    server_name instance.com;
    root /house/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 {
        embrace 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 handle 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 reasonably than HTTP. We will set up certbot utilizing the official directions and use that to put in the SSL certificates robotically 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 prefer 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 all choices proven (Enter 'c' to cancel):

I put in the certificates for each of those and likewise added 301 redirects. Attempt going to instance.com (exchange this together with your internet handle) and it’s best to have the ability to see the shiny padlock proper earlier than the URL. Looks like we had been in a position to efficiently get HTTPS to work!

Safety Points

There are a bunch of issues I skipped on the API aspect. Our API is open to all types of assaults proper now and will be abused by anybody. There is no such thing as a price limiting on the login endpoint. Be sure to observe correct safety practices earlier than placing one thing like this in manufacturing. Or in the event you choose, use a third-party service for person authentication and authorization so that you simply don’t have to fret about a lot of the safety stuff.

Conclusion

That is all that I had deliberate for this submit. You’ll be able to take a look at Miguel’s weblog for extra enjoyable stuff about React and Flask. He’s an authority in relation to 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 creator of the bundle.

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

Word: All of the code from this text will 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