Federation

Federation in Refrain is based on shared trust, which allows users from one server to perform actions on another. This is very much an intentional design decision, as traffic to individual servers does not change with federation. If there is a large, active community on one server, the other serves federated with it do not automatically get a huge increase in traffic. Any kind of proxying that could be done by a user's home server simply doubles the network bandwidth burden of both the home server and the target federated server.

fatclient

So, clients in Refrain are a bit fat. They are required to connect to federated servers when they need to do something on that server. This makes the diagram look a lot worse than it really is; clients are typically only going to fetch data from all servers when they first load, after that they will update their internal stores based on events emitted by the event subscriptions. Media connections are only made when the client actually joins a voice/video session.

To make this access easier on the client, Refrain supports a specific kind of OAuth implementation called Demonstrating Proof of Possession. This method of authentication allows the same authorization token issued by the home server to be used on any federated server. At a very high level, DPoP works like this:

dpop

  • Client generates an ES256 public/private key
  • Client gets a signed refresh token from its home server by logging in with user/pass
  • Client requests a DPoP token by providing its refresh token and its public key
  • Home server signs a new DPoP access token containing the client's public key
  • Anytime client makes a request, it includes a DPoP proof signature signed by its private key as well as its DPoP access token

The DPoP signature states what request is being made, which prevents any attacker from impersonating the user on any other server. All that is required is for the target server to respect the signature of the access token and to validate that the DPoP proof both matches the request and is signed by the same public key in the access token.

Now, all DPoP does it make it possible to prove that the client is who they say they are. What actions they can perform in the federated server is entirely up to the individual server admins to decide on; no permissions from their home servers come along with them. Once two servers become federated, users from ServerA that access ServerB will be allowed in and given a default role assignment. This creates a stub User record in ServerB, so for all intents and purposes they are valid users in ServerB with their own permissions.

🔗Future Improvements

The design of the federation system today is somewhat incomplete. The design was chosen because it was the easiest way to make progress, but it is not suitable for a first release yet. While it does technically work, it has a number of rough patches:

  • user profiles for federated users are not present in the home server
    • this is good, we don't want the user's profile data leaving the home server. however, it does make things like the community member API return incomplete results. The user is present, but the profile data is not, and the client has to fill in the blanks.
  • community invites only work when the user has logged into the federated server at least once
    • due to the nature of the federation system, this will happen whenever the client accesses the system. however, it's quite janky.
  • deleted users still have stubs in the federated servers
    • this is decidedly bad

The fact that federated servers need to know about user removals means we do need to share at least some data throughout the federation. One of the core goals of federation remains not to burdern servers for joining with others, so broadcasting everything everywhere (a la Matrix) is still verboten. Instead, we will simply broadcast the data relevant for federation: user creates/deletes.

We also need to proxy user/profile lookups and searches so that a request to see a federated user profile (like for community members) on the home server calls out to the federated server in realtime. This leads to a federation model that looks someting like this:

dpop