Users¶
Fractal Server's user model and authentication/authorization systems are powered by the FastAPI Users library, and most of the components described below can be identified in the corresponding overview.
UserOAuth
¶
Bases: SQLModel
ORM model for the user_oauth database table.
This class is a modification of
SQLModelBaseUserDB
from fastapi_users_db_sqlmodel.
Original Copyright: 2022 François Voron, released under MIT licence.
Note that several class attributes are
the default ones from fastapi-users
.
| ATTRIBUTE | DESCRIPTION |
|---|---|
id |
TYPE:
|
email |
TYPE:
|
hashed_password |
TYPE:
|
is_active |
If this is
TYPE:
|
is_superuser |
TYPE:
|
is_verified |
If this is
TYPE:
|
oauth_accounts |
TYPE:
|
profile_id |
Foreign key linking the user to a
TYPE:
|
project_dirs |
Absolute paths of the user's project directory. This is used (A) as
a default base folder for the
TYPE:
|
slurm_accounts |
List of SLURM accounts that the user can select upon running a job.
TYPE:
|
Source code in fractal_server/app/models/security.py
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | |
First user¶
To manage fractal-server you need to create a first user with superuser privileges.
This is done by means of the init-db-data command together with the--admin-email/--admin-pwd/--admin-project-dir flags, either during the startup phase or at a later stage.
The most common use cases for fractal-server are:
- The server is used by a single user (e.g. on their own machine). In this case you may simply use the first (and only) user.
- The server has multiple users, and it is connected to one or more SLURM clusters. To execute jobs on a SLURM cluster, a user must be associated to that cluster and to a valid cluster-user via its [
Profile] (more details here).
Authentication¶
Login¶
An authentication backend is composed of two parts:
- the transport, that manages how the token will be carried over the request,
- the strategy, which manages how the token is generated and secured.
Fractal Server provides two authentication backends (Bearer and Cookie), both based the JWT strategy. Each backend produces both /auth/login and /auth/logout routes.
FastAPI Users provides the
logoutendpoint by default, but this is not relevant infractal-serversince we do not store tokens in the database.
Bearer¶
The Bearer transport backend provides login at /auth/token/login
$ curl \
-X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=admin@example.org&password=1234" \
http://127.0.0.1:8000/auth/token/login/
{
"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwiYXVkIjpbImZhc3RhcGktdXNlcnM6YXV0aCJdLCJleHAiOjE3NjI4NzI5MzB9.nLwZHeZCRWSUo5TzaQlho8uMBAf1Fl4XqXSA32lSPJs",
"token_type":"bearer"
}
Cookie¶
The Cookie transport backend provides login at /auth/login
$ curl \
-X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=admin@example.org&password=1234" \
--cookie-jar - \
http://127.0.0.1:8000/auth/login/
# Netscape HTTP Cookie File
# https://curl.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
#HttpOnly_127.0.0.1 FALSE / TRUE 0 fastapiusersauth eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwiYXVkIjpbImZhc3RhcGktdXNlcnM6YXV0aCJdLCJleHAiOjE3NjI5NTg0MDl9.NeB5tie2Mey5w7NkxMhqpablOGBjiKPLncwQT8d5HF4
Authenticated calls¶
Once you have the token, you can use it to identify yourself by sending it along in the header of an API request. Here is an example with an API request to /auth/current-user/:
$ curl \
-X GET \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwiYXVkIjpbImZhc3RhcGktdXNlcnM6YXV0aCJdLCJleHAiOjE3NjI5NTg0MDl9.NeB5tie2Mey5w7NkxMhqpablOGBjiKPLncwQT8d5HF4" \
http://127.0.0.1:8000/auth/current-user/
{
"id": 1,
"email": "admin@example.org",
"is_active": true,
"is_superuser": true,
"is_verified": true,
"group_ids_names": null,
"oauth_accounts": [],
"profile_id": null,
"project_dir": "/tmp/fractal",
"slurm_accounts": []
}
OAuth2¶
Fractal Server also allows a different authentication procedure, not based on the knowledge of a user's password but on external OAuth2 authentication clients.
Through the httpx-oauth library, we currently support OpenID Connect (aka OIDC), GitHub and Google (and more clients can be readily included).
Configuration¶
To use a certain OAuth2 client, you must first register the fractal-server application (see instructions for GitHub and Google).
During app registration, you should provide two endpoints:
- the
Homepage URL(e.g.http://127.0.0.1:8000/), - the
Authorization callback URL(e.g.http://127.0.0.1:8000/auth/github/callback/, wheregithubcould be any client name).
and at the end of this procedure, you will kwnow the Client ID and Client Secret for the app.
Note: You have to enable the "Email addresses" permission for your GitHub registered app, at https://github.com/settings/apps/{registered-app}/permissions. A similar setting may be required for Google.
To add an OAuth2 client, you must provide valid OAuthSettings variables:
OAUTH_CLIENT_NAME=any-name-except-github-or-google
OAUTH_CLIENT_ID=...
OAUTH_CLIENT_SECRET=...
OAUTH_OIDC_CONFIG_ENDPOINT=... # e.g. https://example.org/.well-known/openid-configuration
OAUTH_REDIRECT_URL=... # e.g. https://fractal-web.example.org/auth/login/oauth2
OAUTH_CLIENT_NAME=github
OAUTH_CLIENT_ID=...
OAUTH_CLIENT_SECRET=...
OAUTH_REDIRECT_URL=... # e.g. https://fractal-web.example.org/auth/login/oauth2
OAUTH_CLIENT_NAME=google
OAUTH_CLIENT_ID=...
OAUTH_CLIENT_SECRET=...
OAUTH_REDIRECT_URL=... # e.g. https://fractal-web.example.org/auth/login/oauth2
When fractal-server starts with proper OAuthSettings, two new routes will be generated:
/auth/{OAUTH_CLIENT_NAME}/authorize/,/auth/{OAUTH_CLIENT_NAME}/callback/(theAuthorization callback URLof the client).
Note that the
OAUTH_REDIRECT_URLenvironment variable is optional. It is not relevant for the examples described in this page, since they are all in the command-line interface. However, it is required when OAuth authentication is performed starting from a browser (e.g. through thefractal-webclient), since the callback URL should be opened in the browser itself.
Authorization Code Flow¶
Authentication via OAuth2 client is based on the Authorization Code Flow, as described in this diagram
(adapted from https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow, © 2023 Okta, Inc.)
We can now review how fractal-server handles these steps:
-
Steps 1 → 4
- The starting point is
/auth/client-name/authorize/; - Here an
authorization_urlis generated and provided to the user; - This URL will redirect the user to the Authorization Server (which is e.g. GitHub or Google, and not related to
fractal-server), together with astatecode for increased security; - The user must authenticate and grant
fractal-serverthe permissions it requires.
- The starting point is
-
Steps 5 → 8
- The flow comes back to
fractal-serverat/auth/client-name/callback/, together with the Authorization Code. - A FastAPI dependency of the callback endpoint,
oauth2_authorize_callback, takes care of exchanging this code for the Access Token.
- The flow comes back to
-
Steps 9 → 10
- The callback endpoint uses the Access Token to obtain the user's email address and an account identifier from the Resource Server (which, depending on the client, may coincide with the Authorization Server).
After that, the callback endpoint performs some extra operations, which are not strictly part of the OAuth2 protocol:
- It checks that
stateis still valid; - If the user has never authenticated with
OAuth2before:- it adds to the database a new entry to the
oauthaccounttable, properly linked to theuser_oauthtable; at subsequent logins that entry will just be updated. - it sends a notification of the login to the addresses indicated in
FRACTAL_EMAIL_RECIPIENTS.
- it adds to the database a new entry to the
- It prepares a JWT token for the user and serves it in the Response Cookie.
Full example¶
A given fractal-server instance is registered as a GitHub App, and fractal-server is configured accordingly:
OAUTH_CLIENT_NAME=github
OAUTH_CLIENT_ID=...
OAUTH_CLIENT_SECRET=...
There is a single user, created with the command
fractalctl init-db-data \
--admin-email person@university.edu \
--admin-pwd 1234 \
--admin-project-dir=/tmp/fractal
Now the user wants to log in using her GitHub account associated to the same email.
First, she makes a call to /auth/github/authorize/:
$ curl \
-X GET \
http://127.0.0.1:8000/auth/github/authorize/
{
"authorization_url":"https://github.com/login/oauth/authorize/?
response_type=code&
client_id=...&
redirect_uri=...&
state=...&
scope=user+user%3Aemail"
}
Now the authorization_url must be visited using a browser.
After logging in to GitHub, she is asked to grant the app the permissions it requires.
After that, she is redirected back to fractal-server at /auth/github/callback/, together with two query parameters:
http://127.0.0.1:8000/auth/github/callback/?
code=...&
state=...
The callback function does not return anything, but the response cookie contains a JWT token
"fastapiusersauth": {
"httpOnly": true,
"path": "/",
"samesite": "None",
"secure": true,
"value": "ey..." <----- This is the JWT token
}
The response cookie can be found using the developer tools of the browser, inspecting the response on the network page.
The user can now make authenticated calls using this token, as in
curl \
-X GET \
-H "Authorization: Bearer ey..." \
http://127.0.0.1:8000/auth/current-user/
{
"id": 1,
"email": "person@university.edu",
"is_active": true,
"is_superuser": true,
"is_verified": true,
"group_ids_names": null,
"oauth_accounts": [
{
"id": 1,
"account_email": "person@university.edu",
"oauth_name": "github"
}
],
"profile_id": null,
"project_dir": "/tmp/fractal",
"slurm_accounts": []
}
Authorization¶
On top of being authenticated, a user must be authorized in order to perform specific actions in fratal-server:
/api/alive/is public, and accessible without authentication./auth/current-user/endpoints (including/auth/current-user/profile-info/) require authentication and the user must be active.GET /current-user/allowed-viewer-paths/requires that the user is both active and verified.
/api/v2/endpoints require authentication, and the user must be active and verified and they must have a non-nullprofile_id.- Active superusers can access all
/admin/and/auth/endpoints.