Authentication and Authorization
Gene Panel Builder (GPB) uses TSD's OpenID Connect (OIDC) service to handle authentication (authn). OIDC is a layer on top of of OAuth2, which:
[E]nables Clients to verify the identity of the End-User based on the authentication performed by and Authorization Server, as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner.
OIDC/OAuth 2.0 can handle more than just authn, for instance it can handle authorization (authz) based on user roles, or single signon.
In GPB, we only use a small subset of OIDC features, i.e. authentication, to know if users are who they claim to be. When a user has authenticated with OIDC provider, we create and manage a session with the user info from the provider and then persist the token to a key/value store. The keys on the session store are timestamp signed and used as session IDs, utilizing an encryption key and salt. The session id is set as a session cookie on the browser, with a time to live of 6 hours.
Implementation
Backend
The oauth part of the backend consists of:
- FastAPI router instance providing endpoints for login, oauth callback, session verification, user info querying, and logout
- Session kv-store built with
sqlitedict
python package
Front End
- A session context exists on the frontend for determining whether a user is authenticated or not, as well as fetching user info upon authenticated.
- Logging out makes a request to the backend to expire the session, effectively deleting the session key and value from the session store and the browser session cookie.
Login Flow
Every time a user visits the front end, and there is no gpbuilder session cookie present, or the session cookie has expired, the user is redirected to oauth login screen (TSD). After a user has authenticated with TSD, an authorization code is exchanged between the back end and OIDC service provider, resulting in an ID token and person info being handed back from TSD.
When the front end performs queries against the back end REST service, validity of user session if checked, and if invalid, session cookie is deleted and user redirected to the oauth login.
sequenceDiagram
actor User
participant fe as frontend
participant be as backend
participant oa as OIDC provider
User->>fe: Open GPB
fe->>be: GET /api/auth/verify
alt is authenticated
rect rgba(102, 208, 102, 0.25)
be->>fe: 200
end
else is not authenticated
rect rgba(208, 102, 102, 0.25)
be->>fe: 401 Unauthorized
end
fe->>be: GET /api/auth/login
be->>oa: Request login form
oa->>User: Present login form
User->>oa: Provide credentials
oa->>be: Redirect to callback endpoint
be->>oa: Exchange authentication code to get id token
oa->>be: Return id token
be->>be: Create session ID, and store id token
be->>fe: Set gpb_session cookie value to session ID
fe->>be: GET /api/auth/verify
be->>fe: 200
fe->>be: GET /api/auth/userinfo
be->>fe: {user: "test", email: "test@example.com", name: "Test"}
end
All REST endpoints pertaining to CRUD operations on genepanel data depend on a authenticated
function, injected into FastAPI definitions, that checks the validity of the aforementioned session ID, such that if the ID is no longer valid, an HTTP exception is returned, together with a header to delete the session cookie from the browser, thereby forcing a redirection to the OIDC login form.
Note
When running GPB in development mode, authentication is bypassed.
Requirements
To run the oauth login flow, it is necessary to provide a configuration file secrets/oauth.prod.env
:
SESSION_FILE = <LOCATION_OF_SESSION_STORE_DB_FILE>
CALLBACK_ROUTE = <CALLBACK_ENDPOINT_REGISTERED_WITH_PROVIDER>
OIDC_CLIENT_ID = <OIDC_CLIENT_ID>
OIDC_CLIENT_SECRET = <OIDC_CLIENT_SECRET>
CONF_URL = <OIDC_WELL_KNOWN_CONFIGURATION_URL>
SCOPE = <SCOPES>
DEFAULT_SCOPE = <DEFAULT_SCOPES>
LOGIN_REDIRECT_URL = <URL_WHERE_USER_LOGS_IN>
Explanation of environment variables
SESSION_FILE
: The file path to the session key value store file (sqlite). This will be mounted into the container, which makes it easy to debug locally if need beCALLBACK_ROUTE
: This is the callback endpoint registered with OAuth provider, e.g./api/auth/callback
OIDC_CLIENT
: The client id registered with the providerOIDC_CLIENT_SECRET
: The client secret registered with the providerCONF_URL
: The configuration URL of the provider, typicallyhttps://<domain>/.well-known
SCOPE
: Used t: Default scopesDEFAULT_SCOPE
LOGIN_REDIRECT_URL
: URL to where the user agent logs in, e.g.https://<backend>/api/auth/login
Running in production
Authentication is enabled by default when running production build, but can be turned off by setting environvent variable USE_OAUTH
to false
when starting containers, e.g.
USE_OAUTH=False docker compose -f docker-compose-prod.yml up -d