We wish for flathub.org
to provide a mechanism for developers to log in and
manage their flatpaks and later payment mechanisms etc. Later we will want
non-developers to also be able to log in, in order to manage their subscriptions
or sponsorships etc.
To that end we need the concept of users on flathub.org and to tie that to some authentication mechanism(s). Since developers must have a Github account in order to have submitted a flatpak to flathub, we can use Github to authenticate developers in the first case, but we must not tie authentication there so that later we can offer other mechanisms such as simply email addresses etc.
In order that we can do this, the backend needs a database connection. We are using SQLAlchemy to abstract the DB interface a little, allowing for some flexibility, however for making the production and development environment more consistent we will stand up a postgres server in the docker-compose and link to that for development, preferring a DB in the main flathub postgres cluster for production use.
Ultimately, login flow will be managed via the new frontend being developed, however the backend will drive the full flow in order that the frontend need only render a series of buttons etc. to begin with.
- FlathubUser
- This is the core user concept. We'll store someone's preferred display name here and later be able to tie permissions etc. to this entity
- GithubAccount
- This is the currently only supported mechanism of authenticating a user and will use Github's user id number as the primary key. This means that if a person changes their github username we can still link their login to the right flathub user.
- If a user is currently authenticated, their github oauth token is stored in the table, along with an indication of how recently it was checked for functionality. If we retrieve an old token, we can refresh this and see if it still works, so that we can cause the user to re-authorise if necessary.
- GithubFlowToken
- This is for in-progress oauth flows, rows in here should "expire" after ten minutes.
- NOTE: We do this because it's more secure than handing the mid-session tokens to the user according to authlib docs
- Application
- This is a DB cache of application names, and potentially other metadata though the redis contains most of it
- UserApplicationDevLink
- This is a DB cache of information about what apps a flathubuser has developer access to
- User agent
- The user-agent is the proxy for the person trying to authenticate with us
- This will be the mechanism by which Github or other providers can work out who the user is, and then provide that information to us
- Frontend
- The new frontend https://github.com/flathub/frontend will mediate the authentication/login flow
- Backend
- This backend shall be responsible for executing the flow and managing the database entities
- Github Oauth app
- There shall be an "official" github oauth application for the production site, and another for testing.
- We shall request only
read:user
scope for now, and only if we need it since we're looking to understand whatgithub.org/flathub
repos the user has write access to, for the purpose of identifying apps for the user
Since everything is already namespaced API-wise, and none of the current API benefits from authentication etc. We intend to maintain a separate API for login related work.
All logged in users will have a cookie set for the domain though, this will be
secure-only in production mode, and will be used to identify the user via
their FlathubUser identity. This will be done with the Starlette middleware
called SessionMiddleware
. The secret for the session cookies should be
provisioned in production via a vault secret.
The login flow is basically how we handle logging into the backend from the frontend.
This will always return the login methods available
[
{
"method": "github",
"button": "https://log-in-with-github.png",
"text": "Log in with Github",
}
// ...
]
This starts the login flow for the given method. The method name must match
one returned by GET /login
and the result of calling that will vary from method
to method:
This will start a github oauth flow and unless something goes wrong will return:
{
"redirect": "https://github.com/....",
}
The front end will be responsible for either opening a frame, or redirecting the whole browsing context to that URL, so that the user can log in and authorise flathub.
The frontend will have been directed back to, based on the github flow, and it will be responsible for sending through the parameters to the backend.
They should be supplied to the backend as a JSON POST:
{
"state": "...",
"code": "...",
}
If the oauth flow returned an error, the core parts of that should be passed through:
{
"state": "...",
"error": "...",
}
If the oauth succeeded, The backend will then complete the flow by POSTing to Github in order to exchange the code for a real token, and will complete the flow. Assuming all is well, the session will be updated with the logged in user info, and then a document indicating success will be returned:
{
"status": "ok",
"result": "logged_in",
}
If the oauth failed and an error was provided to the backend, instead it will clean up the in-progress flow and return an error if something was bad (e.g. bad state code) or success if the flow is cleaned up properly:
{
"status": "ok",
"result": "flow_abandoned",
}
Note, the flow being abandoned doesn't mean the user isn't still logged in if this was a flow to add github to an existing flathub user.
This will remove any login information from the session cookie and then return an empty document.
This returns the information about the currently logged in user. If the user is not logged in, it will return a not-authorised code
{
"displayname": "Jeffity Jeff",
"avatar": "https://....",
dev-flatpaks: [
"org.foo.Foo",
"com.bar.Wibble",
// ...
],
}
This returns a confirmation key which encapsulates all the information we hold about the logged in user. It must be used to POST again in order that we know nothing has changed about the user since they requested to delete themselves.
{
"code": "..."
}
The JSON object containing the code must be POSTed to this endpoint. Assuming it is correctly provided and nothing has changed since it was created, the user will be logged out, and all user information removed from the database. This will NOT remove the person's access to github repositories etc.