Skip to content

Instantly share code, notes, and snippets.

@danielsilverstone-ct
Last active February 4, 2022 15:29
Show Gist options
  • Save danielsilverstone-ct/921b13e420dae31ac1f3e636a7eddf52 to your computer and use it in GitHub Desktop.
Save danielsilverstone-ct/921b13e420dae31ac1f3e636a7eddf52 to your computer and use it in GitHub Desktop.
Authentication for Flathub backend

Why have authentication?

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.

Prerequisites

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.

Entities involved in authentication

Database entities

  • 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

System entities

  • 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
  • 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 what github.org/flathub repos the user has write access to, for the purpose of identifying apps for the user

API layout

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.

Login flow

The login flow is basically how we handle logging into the backend from the frontend.

GET /auth/login

This will always return the login methods available

[
    {
        "method": "github",
        "button": "https://log-in-with-github.png",
        "text": "Log in with Github",
    }
    // ...
]

GET /auth/login/{method}

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:

GET /auth/login/github

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.

POST /auth/login/github

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.

POST /auth/logout

This will remove any login information from the session cookie and then return an empty document.

GET /auth/userinfo

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",
      // ...
    ],
}

GET /auth/deleteuser

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": "..."
}

POST /auth/deleteuser

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment