Rails personal project with user management & authentication to proxy serve a private, single-page app.
See blog post "JFMK-Auth Walkthrough' for commentary on the technolgoies.
- Rails 5, Postgres, Selenium, AWS S3, HAML, CoffeeScript, Bootstrap, SCSS
- Docker Compose for development & test; Travis for CI/CD; Heroku Pipelines for production
- 'Simple'
has_secure_password
Rails API for authentication & cookie sessions. User is locked out after X failed attempts. - Authenticated users are served single-page app with a proxied index page, and expiring pre-signed URLs for sensitive S3 hosted content are parsed/injected. Demo content is instance of
jfroom/portfolio-web
. - 'Auto-login' feature can be enabled for a user, which creates a simple and aesthetic url: https://example.com/username. Be aware that using this lowers security threshold. Can implement
has_secure_token
in future if this becomes a concern. - Tests with MiniTest for models & integration, and Capybara Selenium acceptance tests running in a docker service with Chrome standalone.
- VNC locally into the Selenium session to interact and debug.
- Let's Encrypt SSL certificates auto bound to the custom Heroku domain with
letsencrypt-rails-heroku
. - Notify admin by email when user has logged in; sent with SES
- Mailer job runs async in background job with Sucker Punch
- Project initially seeded with
nickjj/orats
Rails template which was very helpful figuring out thedocker-compose
setup. - HoundCI & Rubocop help keep styles consistent
https://jfmk-auth-demo.herokuapp.com
Credentials:
- admin:Admin123
- user:User123
Note:
- In demo mode, new users will not be saved, and existing users will not be updated, or deleted. Users will also not be locked out after repeat fails. This is done in order to keep the users active for future visitors to demo, and to prevent system abuse.
- Demo instance runs on a 'free' dyno server so it is probably sleeping, and may be a little sluggish starting up.
- Install Docker 17.03.0-ce+. This should also install Docker Compose 1.11.2+.
- Verify versions:
docker -v; docker-compose -v;
- Install some docker-compose aliases. I use
docker-compose.plugin.zsh
for oh-my-zsh. (Ex.alias dcup='docker-compose up
) - Install pgAdmin for a SQL gui client.
- Install VNC Viewer to view & interact with selenium sessions that would otherwise be headless.
docker-compose up --build
Build the docker images, install dependencies, and start the services.
Once the docker services are build and running, the web service will occupy the current terminal with the running puma server log. Open a new terminal instance to issue any additional commands.
docker-compose exec web rails db:setup
Set up the database.
docker-compose exec web rails db:seed
Seed the database with two users: admin:Admin123
and user:User123
. Use the admin login to change those immediately.
docker-compose up
Stand up all services. Also installs any new gems if necessary (after switching branches, or pulling from a repo).
open http://localhost:3000/
Visit the web app service.
docker-compose exec web bin/update
Install a new gem, or to run a database migration.
docker-compose down; docker-compose up -d; docker attach jfmkauth_web_1
A common call chain during development to stop any existing/hung containers, stand up all services in detached mode, connect to view web service only (to view running log and interact with byebug). Be aware 'exec' bypasses the entrypoint script which ensures gems are up to date (use docker-compose run --rm ...
instead of docker-compose exec ...
if that is a concern - had to do this for the CI override file).
docker-compose build
If there are changes to the Dockerfile
or the docker-compose
files, the containers may need to be rebuilt.
These docker shortcuts to clean up old docker images/containers/volumes is very handy.
- Launch pgAdmin and configure a connection to:
Host: 0.0.0.0
,Port: 5432
,User: jfmk_auth
,Password: yourpassword
. - Quick tip: To find User table with the seed users: Schemas > Public > Tables, or use the SQL panel.
docker-compose exec web rails test
Run tests (also importantly sets Rails.env = 'test').
vnc://localhost:5900 password:secret
To interactive with and debug Selenium sessions, use VNC to connect to the Selenium service. VNC Viewer works well, and on OS X Screen Sharing app is built-in.
To visit the test app on local machine: open http://localhost:3001/
. To visit in the VNC session visit: http://web:3001
.
Commits to master are automatically tested by Travis CI.
Tests use:
railsware/rack_session_access
with Capybara to conveniently bypass session authenticating which speeds tests up.thoughtbot/climate_control
to adjust ENV vars for models/integration, and a custom/test/backdoor/
route for the capybara test environment only to adjust ENV.
Deploys to a Heroku pipeline with three different apps: staging, demo and production. Currently, releases are infrequent, so just using the Heroku Pipelines GUI to promote staging to demo & prod.
Ultimately would prefer to deploy a Docker image to Heroku — but currently that features is in beta and support for pipelines is spotty.
So for now, Docker and Heroku environments are aligned as closely as possible. Biggest exception being the Dockerfile runs Debian Jessie, and Heroku uses Cedar-14 (Ubuntu 14.04 trusty).
- Install Heroku CLI.
- Use Rails 5 Getting Started guide for first deployment.
source bin/deploy.sh
for iterative deploys.
If the Travis build passes, it will automatically deploy to staging. See .travis.yml.
On the production app's custom domain, Let's Encrypt handles the free SSL certificate with Pixielab's pixielabs/letsencrypt-rails-heroku
gem.
Certificate is good for 90 days. To renew, run or schedule a variation of heroku run -a jfmk-auth rake letsencrypt:renew
.
Bundler installs and keeps track of all the gem libraries. Keeping docker container build times low is not trivial when bundler is involved. It took some time & research to optimize bundler's cache, so is worth an explanation. Credit to the unboxed team for this bundler cache technique.
The web
service uses the Dockerfile
to build itself. It defines an ENTRYPOINT ["/docker-entrypoint.sh"]
bash script which will run the initial bundle install
. Gems are stored in a docker volume called bundle_cache
(see docker-compose.yml
). When any gems are added to the Gemfile
, this entrypoint script will notice and install them into the cache volume. Because of the entrypoint, there is no need to call a special command to do this other than docker-compose up
. This technique is unique because the cache volume will persist across docker image changes, which reduces build times (and increases sanity) during local development.
- S3Auth.com If you want a quick way to just password protect a static S3 website with Basic HTTP Auth, check out S3Auth, and this related article.
- S3 auth proxy. There are a few other project that handle S3 proxy with authentication. But one drawback is the app server becomes a bottleneck — which becomes more obvious for large files like video. A mix of pre-signed S3 expiring private content URLs, and publicly served S3 non-sensitive files (e.g. JS, CSS, some content) alleviates this. Admittedly, the proxy/injection I've cooked up is a little brittle — which leads to my next point.
- Simple content views.
app/controllers/proxy_controller
which parses/proxies/pre-signs S3 content is tightly coupled to my personal needs. If you choose to clone/fork this project for the user management aspect, you'll probably want to yank that controller, related tests, and environment vars. You could just replace it with simple HTML/HAML views. - Devise. In future projects I will use Devise for authentication. Just wanted to write my own first to better understand the auth and user management process.
Copyright © JFMK, LLC Released under the MIT License.