Setup

This guide describes how to get a webapp, derived from baselayer, up and running. A simple example of such an application is provides as a template application.

Clone that application, and then proceed with the following instructions.

Installation

  • A Python 3.8 or later installation is required.

  • Install the following dependencies: Supervisor, NGINX, PostgreSQL, Node.JS

On macOS

  • Using Homebrew: brew install supervisor postgresql node

    • If you want to use brotli compression with NGINX (better compression rates for the frontend), you can install NGINX with the ngx_brotli module with this command: brew tap denji/nginx && brew install nginx-full --with-brotli. If you already had NGINX installed, you may need to uninstall it first with brew unlink nginx. Otherwise, you can install NGINX normally with brew install nginx.

    • Start the postgresql server:

      • to start automatically at login: brew services start postgresql

      • to start manually: pg_ctl -D /usr/local/var/postgres start

  • Using MacPorts: port install nginx +realip postgresql13-server

    • Start the postgresql server: port load postgresql13-server

Port Number Configuration with macOS

The default port number used by the baselayer app is 5000, but this port is not available for use with all operating systems. Port 5000 is not free for the latest macOS version, Monterey.

If 5000 is not available, you will need to modify the config.yaml file to use another port. For example, you may use:

ports:
  app: 5700

See below for more information on modifying the baselayer configuration file.

On Linux

  • Using apt-get: sudo apt-get install supervisor postgresql libpq-dev nodejs

    If you want to use brotli compression with NGINX (better compression rates for the frontend), you have to install NGINX and the brotli module from another source with:

    sudo apt remove -y nginx nginx-common nginx-core
    sudo add-apt-repository ppa:ondrej/nginx-mainline -y
    sudo apt update -y
    sudo apt install -y nginx libnginx-mod-brotli
    

    Otherwise, you can install NGINX normally with sudo apt-get install nginx.

  • It may be necessary to configure your database permissions: at the end of your pg_hba.conf (typically in /etc/postgresql/13.3/main or /var/lib/pgsql/data), add the following lines and restart PostgreSQL (sudo service postgresql restart or systemctl reload postgresql):

    # CONNECTION DATABASE USER ADDRESS METHOD
    host template_app template_app localhost trust
    host template_app template_app_test localhost trust
    host all postgres localhost trust
    

    Substitute the correct database name and user, as defined in your config.yaml.

    If you use IPv6, localhost becomes ::1/128.

Building the baselayer database

  • Initialize the database with make db_init (also tests that your permissions have been properly configured).

  • Run make to start the server and navigate to localhost:5000. If you have modified the baselayer configuration to use a different app port, you should instead navigate to localhost:PORTNUMBER.

Configuration

  • Customize config.yaml (see config.yaml.defaults for all options).

    • Always modify secret_key before deployment!

  • If you want other users to be able to log in:

    • Provide Google auth credentials, obtained as described in config.yaml, or configure another provider (see below).

Sign-in providers

Sign-in is handled by python-social-auth, which ships backends for Google, ORCID, GitHub, Slack, SAML, generic OpenID Connect and many others. With no configuration, Google is used, taking its credentials from server.auth.google_oauth2_key / _secret.

To offer something else, list it under server.auth.backends. Each entry needs the backend’s own name, plus the dotted path to its class. The name is what appears in /login/<name>, and what its SOCIAL_AUTH_<NAME>_* settings are keyed on:

server:
  auth:
    backends:
      - name: orcid
        class: social_core.backends.orcid.ORCIDOAuth2
        key: ...
        secret: ...
        label: Sign in with ORCID
      - name: my-iam
        class: social_core.backends.open_id_connect.OpenIdConnectAuth
        key: ...
        secret: ...
        settings:
          oidc_endpoint: https://iam.example.org

Anything under settings is exported as SOCIAL_AUTH_<NAME>_<KEY>, so a provider speaking standard OpenID Connect needs no code at all. A provider that does need code, for example a bespoke federation endpoint, can subclass social_core.backends.oauth.BaseOAuth2 anywhere importable and be named here by its dotted path; it does not have to live in baselayer.

The login page shows one button per provider, in the order listed. In debug mode, where sign-in succeeds without contacting any provider, the stand-in takes the name of the first provider you list, so development and production exercise the same login page and the same routes.

Signing in with more than one provider

Returning with a provider you have used before always lands you back in the same account, even if you have since changed your email address there.

Returning with a different provider is recognised as the same person only when the second provider reports an email address matching the account and vouches that the address is yours. Otherwise you get a second, separate account.

Most providers say whether they have verified an address, and some verify but never say so. For one you are confident about, add trust_email and its addresses will be accepted for matching:

- name: orcid
  class: social_core.backends.orcid.ORCIDOAuth2
  trust_email: true

Leave it off for anything else. There is also use_unique_user_id, on by default, which keeps accounts tied to the provider’s own id for the user rather than to their email address; there is rarely a reason to change it.

GitHub needs neither: it verifies addresses but python-social-auth drops the flag, so trust_email there would trust something nobody checked. Use the backend below instead, which reports the flag GitHub actually returned:

- name: github
  class: baselayer.app.backends.github.VerifiedEmailGithubOAuth2
  key: ...
  secret: ...

Username generation

When server.auth.username_is_email is set to True (the default), the user’s email address is used as their username.

When set to False, the username is derived from the username field provided by the OAuth provider. For Google OAuth2, this is typically the local part of the email address (everything before @, e.g., john.doe from john.doe@example.com). If that username is already taken, a random suffix is appended until a unique username is found. This logic is handled by the social_core library.

Launch

Launch the app with make run.

Deployment options

The default configuration file used can be overridden by setting the FLAGS environment variable:

FLAGS="--config=myconfig.yaml" make run

Debug mode

By default, baselayer runs in debug mode. In debug mode:

  • The server binds to localhost, not 0.0.0.0 (i.e., is not publicly accessible).

  • Authentication always succeeds, but does not connect to any oauth provider.

  • Code changes cause automatic reloads of the app, and recompilation of Javascript bundles.

When switching to production mode (debug set to False in the config file):

  • The server binds to 0.0.0.0.

  • Javascript bundles are not compiled; they need to be pre-compiled using make bundle.