Skip to content

Full stack microservice Flask / React application, utilizing Docker, Flask-RESTX, Postgres, JWT, GitLab CI/CD, and Pytest.

Notifications You must be signed in to change notification settings

sbathgate/flask-react-auth

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

66 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Authentication with Flask, React, and Docker

pipeline status

Quick start

  • Clone GitLab Repo: $ git clone https://gitlab.com/sbathgate/flask-tdd-docker.git
  • Switch to project root: $ cd flask-tdd-docker/
  • Build the images: $ docker-compose build
  • Run the containers: $ docker-compose up -d
  • Create the database: $ docker-compose exec users python manage.py recreate_db
  • Seed the database: $ docker-compose exec users python manage.py seed_db

File Structure

Within the download you'll find the following directories and files:

β”œβ”€β”€ .gitignore
β”œβ”€β”€ .gitlab-ci.yml
β”œβ”€β”€ Dockerfile.deploy
β”œβ”€β”€ README.md
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ makefile
β”œβ”€β”€ release.sh
└── services
    β”œβ”€β”€ client
    β”‚   β”œβ”€β”€ .dockerignore
    β”‚   β”œβ”€β”€ .eslintrc.json
    β”‚   β”œβ”€β”€ .gitignore
    β”‚   β”œβ”€β”€ Dockerfile
    β”‚   β”œβ”€β”€ Dockerfile.ci
    β”‚   β”œβ”€β”€ README.md
    β”‚   β”œβ”€β”€ coverage
    β”‚   β”œβ”€β”€ package-lock.json
    β”‚   β”œβ”€β”€ package.json
    β”‚   β”œβ”€β”€ public
    β”‚   β”‚   β”œβ”€β”€ favicon.ico
    β”‚   β”‚   β”œβ”€β”€ index.html
    β”‚   β”‚   β”œβ”€β”€ logo192.png
    β”‚   β”‚   β”œβ”€β”€ logo512.png
    β”‚   β”‚   β”œβ”€β”€ manifest.json
    β”‚   β”‚   └── robots.txt
    β”‚   └── src
    β”‚       β”œβ”€β”€ App.jsx
    β”‚       β”œβ”€β”€ components
    β”‚       β”‚   β”œβ”€β”€ About.jsx
    β”‚       β”‚   β”œβ”€β”€ AddUser.jsx
    β”‚       β”‚   β”œβ”€β”€ LoginForm.jsx
    β”‚       β”‚   β”œβ”€β”€ Message.jsx
    β”‚       β”‚   β”œβ”€β”€ NavBar.css
    β”‚       β”‚   β”œβ”€β”€ NavBar.jsx
    β”‚       β”‚   β”œβ”€β”€ RegisterForm.jsx
    β”‚       β”‚   β”œβ”€β”€ UserStatus.jsx
    β”‚       β”‚   β”œβ”€β”€ UsersList.jsx
    β”‚       β”‚   β”œβ”€β”€ __tests__
    β”‚       β”‚   β”‚   β”œβ”€β”€ About.test.jsx
    β”‚       β”‚   β”‚   β”œβ”€β”€ AddUser.test.jsx
    β”‚       β”‚   β”‚   β”œβ”€β”€ App.test.jsx
    β”‚       β”‚   β”‚   β”œβ”€β”€ LoginForm.test.jsx
    β”‚       β”‚   β”‚   β”œβ”€β”€ Message.test.jsx
    β”‚       β”‚   β”‚   β”œβ”€β”€ NavBar.test.jsx
    β”‚       β”‚   β”‚   β”œβ”€β”€ RegisterForm.test.jsx
    β”‚       β”‚   β”‚   β”œβ”€β”€ UserStatus.test.jsx
    β”‚       β”‚   β”‚   β”œβ”€β”€ UsersList.test.jsx
    β”‚       β”‚   β”‚   └── __snapshots__
    β”‚       β”‚   β”‚       β”œβ”€β”€ About.test.jsx.snap
    β”‚       β”‚   β”‚       β”œβ”€β”€ AddUser.test.jsx.snap
    β”‚       β”‚   β”‚       β”œβ”€β”€ App.test.jsx.snap
    β”‚       β”‚   β”‚       β”œβ”€β”€ LoginForm.test.jsx.snap
    β”‚       β”‚   β”‚       β”œβ”€β”€ Message.test.jsx.snap
    β”‚       β”‚   β”‚       β”œβ”€β”€ NavBar.test.jsx.snap
    β”‚       β”‚   β”‚       β”œβ”€β”€ RegisterForm.test.jsx.snap
    β”‚       β”‚   β”‚       β”œβ”€β”€ UserStatus.test.jsx.snap
    β”‚       β”‚   β”‚       └── UsersList.test.jsx.snap
    β”‚       β”‚   └── form.css
    β”‚       β”œβ”€β”€ index.js
    β”‚       └── setupTests.js
    β”œβ”€β”€ nginx
    β”‚   └── default.conf
    └── users
        β”œβ”€β”€ .coverage
        β”œβ”€β”€ .coveragerc
        β”œβ”€β”€ .dockerignore
        β”œβ”€β”€ Dockerfile
        β”œβ”€β”€ Dockerfile.prod
        β”œβ”€β”€ entrypoint.sh
        β”œβ”€β”€ htmlcov
        β”œβ”€β”€ manage.py
        β”œβ”€β”€ project
        β”‚   β”œβ”€β”€ __init__.py
        β”‚   β”œβ”€β”€ api
        β”‚   β”‚   β”œβ”€β”€ __init__.py
        β”‚   β”‚   β”œβ”€β”€ auth.py
        β”‚   β”‚   β”œβ”€β”€ ping.py
        β”‚   β”‚   └── users
        β”‚   β”‚       β”œβ”€β”€ __init__.py
        β”‚   β”‚       β”œβ”€β”€ admin.py
        β”‚   β”‚       β”œβ”€β”€ crud.py
        β”‚   β”‚       β”œβ”€β”€ models.py
        β”‚   β”‚       └── views.py
        β”‚   β”œβ”€β”€ config.py
        β”‚   β”œβ”€β”€ db
        β”‚   β”‚   β”œβ”€β”€ Dockerfile
        β”‚   β”‚   └── create.sql
        β”‚   └── tests
        β”‚       β”œβ”€β”€ __init__.py
        β”‚       β”œβ”€β”€ conftest.py
        β”‚       β”œβ”€β”€ pytest.ini
        β”‚       β”œβ”€β”€ test_admin.py
        β”‚       β”œβ”€β”€ test_auth.py
        β”‚       β”œβ”€β”€ test_config.py
        β”‚       β”œβ”€β”€ test_ping.py
        β”‚       β”œβ”€β”€ test_user_model.py
        β”‚       β”œβ”€β”€ test_users.py
        β”‚       └── test_users_unit.py
        β”œβ”€β”€ requirements-dev.txt
        β”œβ”€β”€ requirements.txt
        └── setup.cfg

Common Commands

Docker Compose

Set the REACT_APP_USERS_SERVICE_URL environment variable:

$ export REACT_APP_USERS_SERVICE_URL=http://localhost:5001

Build the images:

$ docker-compose build

Build and spin up the new containers:

$ docker-compose up -d --build

To stop the containers:

$ docker-compose stop

To bring down the containers:

$ docker-compose down

Client

Run the tests without coverage:

$ docker-compose exec client npm test

Run the tests with coverage:

$ docker-compose exec client npm test --coverage

Check formatting with Prettier:

$ docker-compose exec client npm run prettier:check

Lint with eslint:

$ docker-compose exec client npm run lint

Server

Create the database:

$ docker-compose exec users python manage.py recreate_db

Seed the database:

$ docker-compose exec users python manage.py seed_db

Run the tests without coverage:

$ docker-compose exec users python -m pytest "project/tests" -p no:warnings

Run the tests with coverage:

$ docker-compose exec users python -m pytest "project/tests" -p no:warnings --cov="project"

Lint with Flake8:

$ docker-compose exec users flake8 project

Run Black and isort with check options:

$ docker-compose exec users black project --check

$ docker-compose exec users /bin/sh -c "isort project/**/*.py --check-only"

Make code changes with Black and isort:

$ docker-compose exec users black project

$ docker-compose exec users /bin/sh -c "isort project/**/*.py"

Postgres

Want to access the database via psql?

$ docker-compose exec users-db psql -U postgres

Then, you can connect to the database and run SQL queries. For example:

# \c users_dev # select * from users;

Other Commands

Want to force a build?

$ docker-compose build --no-cache

Remove images:

$ docker rmi $(docker images -q)

TODO:

My notes:

  • Configure singular setup.cfg for flake8, black and isort.

Test-Driven Development with Python, Flask and Docker

  • Database migrations: Manage changes to the database through SQLAlchemy database migrations with the Flask-Migrate extension.

Authentication with Flask, React, and Docker

Part 3: React Auth - Part 2
  • Write test to ensure UserStatus redirects to login if invalid token.
  • EXPLORE: For added protection, instead of storing refresh tokens in LocalStorage, how would you return tokens from the server in HttpOnly cookies? The Flask-JWT-Extended extension may be worth looking at.
Part 3: React Alert Messages
  • Add test to ensure message disappears when 1: a user click the 'x', 2: a new message is flashed, 3: three seconds pass
Part 3: Update Components
  • Prevent currently logged in person from deleting themselves.
Part 3: Next Steps
  • Test coverage: Add more tests to increase the overall test coverage, making sure to cover any remaining edge cases.
  • Unit tests: Add unit tests (via monkeypatch) to cover the auth routes.
  • DRY out the code: There's plenty of places in the code base that could be refactored.
  • Flask CORS: Instead of allowing requests from any domain, lock down the Flask service by only allowing requests that originate from the Heroku domain.
  • Caching: Add caching (where appropriate) with Flask-Cache.
  • Duplicate usernames: Prevent duplicate usernames in the database.
  • Invalidate refresh tokens: Users can have a number of active refresh tokens. It may be worth controlling this to prevent abuse by only allowing a user to have a single refresh token at time. Create a database table for this and update the client and server-side logic.
  • Blacklist tokens: You may want to create a database table for used access and refresh tokens to prevent abuse. Again, update the client and server-side as necessary.
  • Role based authorization: Add role based authorization. Refer to the "Auth" section in Awesome Flask for more info.
  • Cross tab logout: Incorporate cross browser tab logout by adding an event listener on the refresh token in LocalStorage.
  • Transactional emailing: Add the ability to send transactional emails for email confirmation and password changes.
  • Client side: Add the ability to update a user using the same modal configured for adding a user and prevent the currently logged in user from deleting themselves in the table.
  • Hooks: Refactor the class-based React components to functions with React Hooks. Refer to Primer on React Hooks for more info on Hooks.

Valuable Notes

If you get a compilation error due to Module not found: Can't resolve 'temp'; try installing temp in the running container: $ docker-compose exec client npm install react-router-dom

About

Full stack microservice Flask / React application, utilizing Docker, Flask-RESTX, Postgres, JWT, GitLab CI/CD, and Pytest.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published