Skip to content

GitLab

    • GitLab: the DevOps platform
    • Explore GitLab
    • Install GitLab
    • How GitLab compares
    • Get started
    • GitLab docs
    • GitLab Learn
  • Pricing
  • Talk to an expert
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
    • Switch to GitLab Next
    • Menu
    Projects Groups Snippets
  • Sign up now
  • Login
  • Sign in / Register
  • F Fediventure
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
    • Locked Files
  • Issues 10
    • Issues 10
    • List
    • Boards
    • Service Desk
    • Milestones
    • Iterations
    • Requirements
  • Merge requests 0
    • Merge requests 0
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
    • Test Cases
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages & Registries
    • Packages & Registries
    • Package Registry
    • Container Registry
    • Infrastructure Registry
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Code review
    • Insights
    • Issue
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar

GitLab 15.0 is launching on May 22! This version brings many exciting improvements, but also removes deprecated features and introduces breaking changes that may impact your workflow. To see what is being deprecated and removed, please visit Breaking changes in 15.0 and Deprecations.

  • Fediventure
  • Fediventure
  • Issues
  • #1
Closed
Open
Created Jan 03, 2021 by infowski@infowskiDeveloper0 of 1 task completed0/1 task

"Matrixless" federation protocol proposal

semi-WIP, but up for comments

Basic federation proposal

Glossary

  • instance - full standalone WorkAdventure deployment, including front, back, pusher services, etc...
  • local instance - deployment of WorkAdventure a user has originally authenticated into (currently only anonymous users are suported)
  • remote instance - deployment of WorkAdventure other than local instance where user may travel to
  • map server - HTTP(s) server hosting Tiled JSON files that describe WorkAdventure rooms
  • instance URL - single base URL where following WorkAdventure endpoints are exposed:
    • /room - room WebSocket connection
    • /token/exchange - user token exchange
    • /.well-known/workadventure-jwks - instance token signing JWK Set endpoint

"Instance" hosts its own frontend, authentication/avatar service (/anonymLogin and /verify) and pusher service

"Map" can be hosted anywhere, as long as it is cors-accessible. People present on a specific map need to use the same pusher service to see each other (or have their pushers share state across federated instances).

Proposal

Pusher service access

In order to exchange current map / player state pusher service needs to be exposed to all web origins (CORS).

Traveling between instances

When trying to enter a specific map frontend will determine address of an instance server that is used to host said map based on its URL - ${url.protocol}://${url.host} should be the default instance URL. When traveling between instances frontend service will be always hosted from local instance URL and only WebSocket connection will be accessing remote instance.

Split between map and instance server

As some people may want to only host a map without a full WorkAdventure instance, a map file can link a specific instance URL that needs to be used instead of an autogenerated one. Instance can be configured to limit specific maps that can be used by inspecting /room?roomId= WebSocket query parameter. Technical aspects of this part need to be specified further later.

Instance authentication

Note: Currently only "anonymous" instance user authentication is supported, however some proper authentication providers (local username/password database, external OAuth2, LDAP, SAML...) can be implemented. This is out of scope of this proposal.

User authentication ends with frontend receiving a "local" token. This token is used when connecting with local pusher service WebSocket (?token query parameter). Said token should be treated as a opaque string by a frontend service (for now).

Before opening a new WebSocket connection a frontend service needs to exchange an existing "local" token with "remote" token intended to be used on a specific remote instance. This can be carried out using a HTTP request sent to local instance (pusher service). Said token can be cached (TODO: how long?) in frontend in order to reduce HTTP traffic when traveling between maps.

Internally, mentioned local/remote tokens internally are assymetrically-signed JWT tokens that contain following fields:

  • iss - string, source instance URL (eg. "https://world.hackerspace.pl")
  • sub - string, unique instance user ID (eg. "123456")
  • exp - number, token expiration date
  • aud - string, target instance URL (can be ommited for "local" tokens, needs to be equal to remote instance URL for "remote" tokens)
  • name - string, player name (optional, default to sub)

A pusher service, when presented with a token needs to:

  • check iss token field:
    • if iss equals its own instance URL, it needs to verify against a local public key
    • otherwise it fetches (and caches for 15 minutes) JWK Set at URL ${iss}/.well-known/workadventure-jwks (??) and uses it to verify the token signature against
  • check aud token field:
    • if aud exists and does not match current instance URL, token needs to be considered invalid
  • check exp token field - if exp field value is lower than current timestamp, token needs to be considered invalid
  • when all checks pass, pusher service can use name and sub values as a profile descriptor to present to other players

Token exchange

"Local" token can be exchanged for "remote" token. Token exchange endpoint accepts following arguments:

  • token - existing local token
    • iss field in the token needs to be equal to local instance URL (one that is handling the request)
    • exp field needs to be valid (ie. token not expired)
    • aud field needs to be missing or equal to iss
    • Token needs to be properly signed by local instance
  • instance - target remote instance URL

Result of that operation is a new token with aud field set to value of instance request argument. Additional token fields MAY get changed (eg. when user display name has changed)

Notable workadventure changes to get some basic federation implemented

  • Step 0: (this is being tracked in #2)
    • expose pusher service publicly via CORS (code OPTIONS handler...?)
    • disable JWT token verification (code / code)
    • modify front to jump to different wss:// URL based on map URL (code)
  • Step 1:
    • expand JWT token parsing in pusher service (allow externally-signed tokens) (code)
      • code seems like "userUUid"/"clientUUid" is only ever passed as a plain string - this needs to be generated based on incoming user sub/iss maybe?
    • exchange existing token with local pusher service into one with specified aud field and use that to access specific remote instance (prevent impersonation by malicious instance) (code / code)
  • Step 2:
    • make pusher use display name extracted from a token instead of name field and display source instance name of a specific user (code)

Possible blockers

  • Need to research how Jitsi embedding works - users on remote instances will use Jitsi configured on a remote instance - does Jitsi frontend verify which origin it is embedded on? Jitsi authentication tokens are handed out by remote instance pusher, so that shouldn't be a problem.
Edited Jan 17, 2021 by infowski
Assignee
Assign to
Time tracking