This app is a repository that mantains data of English translations of brazilian literature books.
Access backend app docs here
Original book
represent a Brazilian literature book
- has one
title
- has many
authors
- is identified by its
title
and itsauthors
Translated book
represents an English translation of Original book
- has one
original book
- has many
authors
- has many
publications
(the same publication, republished) - is identified by its
authors
and itsoriginal book
Publication
is a single publication of a translated book
- has one
title
- has one
country
- has one
year
- has one
publisher
- is identified by all of its fields
Author
is the author of a book, be it original or translated
- has one
name
- is identified by its
name
Authentication is currently implemented using Google as identity provider. The OAuth2 flow is implemented in the frontend server using NextAuth. To integrate with your environment, you should follow these steps:
- create a new project in the Google Cloud Console;
- configure the OAuth consent screen in order to generate the credentials;
- create new OAuth2 credentials, ideally one per environment, setting the site's canonical URL as allowed JavaScript origin and the
/api/auth/callback/google
api path as allowed redirect URI; - and generate OAuth2 credentials through the credentials dashboard. You will need to configure the OAuth consent screen in order to generate the credentials. This will generate a brand new Google OAuth2 Client whose Client ID and Secret must be passed to the app via environment variables.
Authentication is achieved with JSON Web Tokens (JWT). An id token is retrieved and stored on sign in by the frontend server using Google's OAuth2 flow. The whole authentication flow is performed by the frontend server, which forwards the access token to the browser so it can do further client-side requests. The access token is sent to the backend server on each request via authorization header for verification.
The id token is verified in the backend server checking its issuer (that can be retrieved from Google's well-known OpenId Configuration); its audience (the configured Google OAuth2 client, identified by its id); and its cryptographic signature (using one of Google's OAuth2 certificates as public key).
The OpenId configuration and the OAuth2 certificates) are referred as auth configuration. This configuration is fetched and stored in the application's environment on the backend server's startup. Although is unlikely for these to change, a stale configuration situation can be solved by restarting the server.
Authorization is role-based, and the domain defines three user roles: admin
, contributor
and reader
. Currently, admin
is the only relevant role, being the only one allowed to sign into the app. Every endpoint is secured behind the authorization layer, except GET /publications
. The only endpoint that does not require admin
privileges is POST /users
. However, it requires authentication, and will only allow to operate on the authenticated user (see more). Authorization is also performed in the frontend, as non-admin users to won't be able to procede with sign in once their role is verified.
Users are created automatically on their sign in, with the reader
role by default. Only email
, subject_id
and role
are stored. The subject_id
is an identifier provided by google and serves as the user's key: email addresses and their plus-sign aliases, like example@gmail.com
and example+richardburton@gmail.com
are linked to the same subject_id
and won't trigger redudant user creation that could be exploited. The only way to change a user's role is through the database.
Users are retrieved during the sign in process for role verification in a "get or insert" fashion: a POST /users
is issued once the id token is received from google. The expected outcome is a 201 CREATED
response with the user data (email
and role
) on the first sign in; and a 409 CONFLICT
response with the user data (email
and role
) if a user with the subject_id
present in the id token already exists. Other responses will interrupt the sign in flow.
Environment variables are configuration units relevant to the app's build or runtime environment, rather than the code itself.
Key | Description | Recommended value for dev |
---|---|---|
NEXT_PUBLIC_API_URL |
URL of the backend server API | http://localhost:4000/api |
NEXT_PUBLIC_GOOGLE_RECAPTCHA_SITEKEY |
Sitekey from Google Recaptcha | Sign up for one in http://www.google.com/recaptcha/admin |
NEXT_INTERNAL_API_URL |
URL of the backend server API. For use in a closed environment. Set to the same value of NEXT_PUBLIC_API_URL if Phoenix server public url is reachable from NextJS server. |
http://localhost:4000/api |
NEXTAUTH_URL |
The canonical URL of the site (read more) | http://localhost:3000 |
NEXTAUTH_SECRET |
Secret for JWT encryption | Generate with openssl rand -base64 32 |
NEXT_PORT |
NextJS port | 3000 |
GOOGLE_CLIENT_ID |
Google OAuth2 client id | Get from google, see authentication |
GOOGLE_CLIENT_SECRET |
Google OAuth2 client secret | Get from google, see authentication |
Key | Description | Recommended value for dev |
---|---|---|
GOOGLE_CLIENT_ID |
Google OAuth2 client id | Get from google, see authentication |
GOOGLE_OPENID_CONFIG_URL |
URL of Google's OpenId configuration | https://accounts.google.com/.well-known/openid-configuration |
GOOGLE_OAUTH2_CERTS_URL |
URL of Google's OAuth2 certificates | https://www.googleapis.com/oauth2/v2/certs |
PGUSER |
Postgres user | postgres |
PGPASSWORD |
Postgres password | postgres |
PGDATABASE |
Postgres database | richard_burton_dev |
PGPORT |
Postgres port | 3542 |
PHX_HOST |
Phoenix host | localhost |
PHX_PORT |
Phoenix port | 4000 |
PHX_SECRET_KEY_BASE |
Phoenix secret key base | Generate with mix phx.gen.secret |
PHX_CONSUMER_URL |
URL of the frontend api. This is used to configure CORS headers | http://localhost:3000 |
POSTGREX_TIMEOUT |
Timeout in milliseconds for database requests. Optional. Default is 15000. Must be available in compile time. | 15000 |
GOOGLE_RECAPTCHA_SECRET_KEY |
Sitekey from Google Recaptcha | Sign up for one in http://www.google.com/recaptcha/admin |
GOOGLE_RECAPTCHA_VERIFICATION_URL |
URL to verify Google Recaptcha tokens | https://www.google.com/recaptcha/api/siteverify |
SMTP_HOST |
SMTP relay host | |
SMTP_PORT |
Port used to connect to the configured SMTP service | |
SMTP_USER |
User to be used to send emails through the configured SMTP service | |
SMTP_PASS |
Password of the user to be used to send emails through the configured SMTP service | |
SMTP_FROM |
Default name to be used in behalf of the application | |
SMTP_NAME |
Default name to be used in behalf of the application | Richard & Isabel Burton |
SMTP_TLS |
Wether TLS is used in the communication to the SMTP service. Can be always , never or if_available |
if_available |
SMTP_ADMIN_INBOX |
Administrative inbox, for example, to receive "Contact Form" messages. |
It's possible to run the development server using Docker, with fast reload enabled both in the frontend and in the backend server. You must have Docker installed for this to work. Environment variables, except secrets, are already set with recommended values in docker.compose.dev.yml
. You must provide secret values in a .env.development.local
file in the project's root with the following environment variables:
GOOGLE_CLIENT_ID=value
GOOGLE_CLIENT_SECRET=value
GOOGLE_OPENID_CONFIG_URL=value
GOOGLE_OAUTH2_CERTS_URL=value
We recommend using GNU Make
as command line tool for running the server without having to specify all the configuration flags in docker compose up
command. Defined make
targets for the development environments are:
Command | Description |
---|---|
make dev |
Run the whole app (NextJS, Phoenix and Postgres) as separated containers in a docker project |
make dev_frontend |
Run the app's frontend (NextJS) as a docker container |
make dev_backend |
Run the apps's backend (Phoenix and Postgres) as containers in a docker project |
make dev_phoenix |
Run the Phoenix server as a docker container |
Once the Phoenix container is running, run database migrations with the following command:
docker exec richard-burton-dev-phoenix-1 mix ecto.migrate
You must have Node, NPM, Erlang, Elixir installed and a Postgres database configured as specified in backend/config/dev.ex
. We recommend using asdf
to manage Elixir and Erlang versions, which are specified for this project in backend/.tool-versions
- Configure secrets as described below
- Start your
postgres
database - Navigate to the
backend
folder - Install dependencies with
mix deps.get
- Create and migrate your database with
mix ecto.setup
- Start Phoenix endpoint with
mix phx.server
or inside IEx withiex -S mix phx.server
Now you can visit localhost:4000
from your browser.
To configure backend secrets in development mode, you should create a file named dev.local.exs
in backend/config
and add the relevant configuration for google_*
and smtp_*
. These variables are the same as those described in the environment variables section, although downcased. phx_consumer_id
is already predefined in config/dev.exs
, but it can be overrided by defining a value in dev.local.exs
.
The configuration file must follow this format:
Import Config
config :richard_burton,
google_cliente_id: "value",
google_openid_config_url: "value",
google_oauth2_certs_url: "value",
- Configure secrets as described below
- Navigate to the
frontend
folder - Set the environment variables in your
.env
or.env.development
file: - Install dependencies with
npm i
- Start NextJS server with
npm run dev
Now you can visit localhost:3000
from your browser.
To configure frontend secrets in development mode, you should create a file named .env.development.local
in the frontend
folter and add the relevant configuration for GOOGLE_CLIENT_ID
and GOOGLE_CLIENT_SECRET
. These variables are described in the environment variables section. Other relevant variables for the frontend's development environment are predefined in .env.development
and can be overridden by resetting them in env.development.local
.
The configuration file must follow this format:
GOOGLE_CLIENT_ID=value
GOOGLE_CLIENT_SECRET=value
The app is intended to be deployed using Docker. Configuration is included in the docker-compose.prod.yml
at the project's root. All the environment variables must be defined in a .env.production.local
file in the project's root.
We recommend using GNU Make
as command line tool for running the server without having to specify all the configuration flags in docker compose up
command. Defined make
targets for the development environments are:
Command | Description |
---|---|
make prod |
Run the whole app (NextJS, Phoenix and Postgres) as separated containers in a docker project |
make prod_frontend |
Run the app's frontend (NextJS) as a docker container |
make prod_backend |
Run the apps's backend (Phoenix and Postgres) as containers in a docker project |
make prod_phoenix |
Run the Phoenix server as a docker container |
Once the Phoenix container is running, run database migrations with the following command:
docker exec richard-burton-prod-phoenix-1 bin/migrate
This app provides a mix task to initialize the database from a CSV file, data.csv
placed on the project's root directory. This project should already include such file. Run the task with mix rb.load_data
.
A data.csv
entry is a Publication
, with their associated entities embedded. The fields, ordered, are:
original book authors
(separated by commas)publication year
publication country
original book title
publication title
publication authors
(separated by commas)publication publisher