profilefed

package module
v0.0.0-...-7161f64 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 18, 2024 License: MPL-2.0 Imports: 17 Imported by: 0

README

ProfileFed

Go Reference Go Report Card

ProfileFed is a simple, lightweight federation protocol for platforms that host user profiles. This repository contains a reference implementation of the protocol, written in Go.

Specification

WebFinger

ProfileFed endpoints are discovered via WebFinger. ProfileFed WebFinger responses must contain a link with a rel of self and a type of application/x-pfd+json. Requests to the URL contained within the href of that link must return a Profile Descriptor as defined below.

Profile Descriptor (PFD)

This object represents a user profile returned by a ProfileFed server in response to a request to the ProfileFed URL discovered via WebFinger, as defined above.

This response must include a message signature. It should be transferred via the X-ProfileFed-Sig header, which must contain an Ed25519 signature of the response encoded in base64. The message must be verified against this signature before any further processing takes place. If the signature does not match, the response must be ignored and an error must be returned.

If the all query parameter is set to 1 in the request, the server must return all the profiles it has for the user, encoded as a JSON object with arbitrary ID strings mapped to profile descriptors. If the optional id query parameter is set to a specific descriptor ID, the server should respond with the corresponding profile. If no id is provided, the server may decide which profile to respond with.

The response should use the MIME type application/x-pfd+json.

Profile Descriptor Object:

Property Type Description
id string Arbitrary ID string for the profile
namespaces []string List of namespaces used in the profile
display_name string User's preferred display name
username string User's username
bio string User's bio text
role string User's role on the server
extra []extra Additional user data defined by namespaces

If role is empty or not provided, user should be assumed

The namespace URLs should point to human-readable documentation of the types and data that can be used in the objects that they define.

Possible values for role are server_host, admin, moderator, developer, or user. The server can arbitrarily decide which roles apply to the user. If the user has multiple roles, they should be delimited by commas. If any other custom roles are required, they should be specified in extra and defined in a custom namespace.

extra Object:

Property Type Description
namespace string The namespace URL used in this object
type string The type of data described by this object
data any Arbitrary custom data

The namespace can be any URL that's defined in the namespaces array. The URL fragment is ignored when checking if the namespace is defined.

The type can be any arbitrary string describing the data, for example: category, donation_url, etc.

Server Info

This object represents information about a server in response to a server info request. It must be returned in respoonse to a request to /_profilefed/server. The host and port of the URL discovered via WebFinger will be used to make this request.

The pubkey should be stored to check against further responses.

This response must include a message signature. It should be transferred via the X-ProfileFed-Sig header, which must contain an Ed25519 signature of the response encoded in base64. Except for the first time a server is contacted, the message must be verified against this signature before any further processing takes place. If the signature does not match, the response must be ignored and an error must be returned.

If the server switches to a new key, this message must be signed with every previously-used key. These signatures must be provided in X-ProfileFed-Previous headers, encoded as base64. If the public key doesn't match and there are no matching signatures, any responses signed with the new key must return an error and must not be processed.

Properties:

Property Type Description
server_name string Name of the server
previous_names array List of previous names used by the server
pubkey string Base64-encoded Ed25519 public key of the server

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrPubkeyNotFound signifies that the server public key is not found.
	ErrPubkeyNotFound = errors.New("server pubkey not found")
	// ErrNoSignature signifies that the response contains no signature.
	ErrNoSignature = errors.New("response contains no signature")
	// ErrSignatureMismatch signifies that the message does not match the server signature.
	ErrSignatureMismatch = errors.New("message does not match server signature")
)
View Source
var ErrDescriptorNotFound = errors.New("descriptor not found")

ErrDescriptorNotFound should be returned

Functions

func LoadOrGenerateKeys

func LoadOrGenerateKeys(path string) (ed25519.PublicKey, ed25519.PrivateKey, error)

LoadOrGenerateKeys checks whether the file at path exists. If it does, the private and public keys at that path are loaded and returned. If not, new keys are generated and saved to the given path.

func LoadPrivateKey

func LoadPrivateKey(path string) (ed25519.PrivateKey, error)

LoadPrivateKey loads a private Ed25519 key from the given path.

func LoadPrivateKeys

func LoadPrivateKeys(paths ...string) []ed25519.PrivateKey

LoadPrivateKeys loads the private keys at all the provided paths.

Any invalid keys are skipped.

Types

type Client

type Client struct {
	// SavePubkey saves the public key for a given server.
	SavePubkey func(serverName string, previousNames []string, pubkey ed25519.PublicKey) error

	// GetPubkey retrieves the public key for a given server.
	// If the key isn't found, GetPubkey should return [ErrPubkeyNotFound]
	GetPubkey func(serverName string) (ed25519.PublicKey, error)
}

Client represents a ProfileFed client

func DefaultClient

func DefaultClient() Client

DefaultClient returns a default client for ProfileFed.

It uses an in-memory synchronized map to store public keys. For production, it's highly recommended to implement a custom client that persists the keys to a database or similar, so that restarting your app doesn't provide opportunities for malicious servers.

func (Client) Lookup

func (c Client) Lookup(resource string) (*Descriptor, error)

Lookup looks up the profile descriptor for the given resource.

func (Client) LookupAll

func (c Client) LookupAll(resource string) (map[string]*Descriptor, error)

Lookup looks up all the available profile descriptors for the given resource.

func (Client) LookupAllWebFinger

func (c Client) LookupAllWebFinger(wfdesc *webfinger.Descriptor) (map[string]*Descriptor, error)

LookupAllWebFinger is the same as Client.LookupAll, but it accepts an existing WebFinger descriptor rather than looking one up.

func (Client) LookupID

func (c Client) LookupID(resource, id string) (*Descriptor, error)

LookupID looks up the profile descriptor that matches the given ID for the given resource.

func (Client) LookupWebFinger

func (c Client) LookupWebFinger(wfdesc *webfinger.Descriptor) (*Descriptor, error)

LookupWebFinger is the same as Client.Lookup, but it accepts an existing WebFinger descriptor rather than looking one up.

func (Client) LookupWebFingerID

func (c Client) LookupWebFingerID(wfdesc *webfinger.Descriptor, id string) (*Descriptor, error)

LookupWebFingerID is the same as Client.LookupID, but it accepts an existing WebFinger descriptor rather than looking one up.

type Descriptor

type Descriptor struct {
	// ID is an arbitrary ID string for the profile.
	ID string `json:"id"`
	// Namespaces is a list of namespaces used in the profile.
	Namespaces []string `json:"namespaces"`
	// DisplayName is the user's preferred display name.
	DisplayName string `json:"display_name"`
	// Username is the user's username.
	Username string `json:"username"`
	// Bio is the user's bio text.
	Bio string `json:"bio"`
	// Role is the user's role on the server. If not set,
	// [RoleUser] is assumed.
	Role Role `json:"role"`
	// Extra is additional user data defined by namespaces
	Extra []Extra `json:"extra"`
}

Descriptor represents a ProfileFed descriptor

func (*Descriptor) AddExtra

func (d *Descriptor) AddExtra(namespace, etype string, data any) error

AddExtra is a convenience function that adds an extra data object to the descriptor. It defines any undefined namespaces and marshals the data parameter into JSON.

type Extra

type Extra struct {
	// Namespace is the namespace URL used in this object
	Namespace string `json:"namespace"`
	// Type is an arbitrary string that represents the type of
	// data in the Data field.
	Type string `json:"type"`
	// Data is the arbitrary additional user data
	Data json.RawMessage `json:"data"`
}

Extra represents additional user data defined by namespaces

type Handler

type Handler struct {
	// PrivateKey contains the server's Ed25519 private key for signing responses
	PrivateKey ed25519.PrivateKey

	// AllDescriptorsFunc should return all the profile descriptors known to the server.
	// If no matching descriptors can be found, AllDescriptorsFunc should reutnr
	// [ErrDescriptorNotFound].
	AllDescriptorsFunc func(req *http.Request) (map[string]*Descriptor, error)

	// DescriptorFunc should return a single descriptor. Make sure to check the `id`
	// query parameter if your user has several descriptors available. If a matching
	// descriptor cannot be found, DescriptorFunc should return [ErrDescriptorNotFound].
	DescriptorFunc func(req *http.Request) (*Descriptor, error)

	// ErrorHandler is called whenever an error is encountered.
	ErrorHandler func(err error, res http.ResponseWriter)
}

func (Handler) ServeHTTP

func (h Handler) ServeHTTP(res http.ResponseWriter, req *http.Request)

ServeHTTP implements the http.Handler interface

type Role

type Role string

Role represents a user's role on a server

const (
	RoleServerHost Role = "server_host"
	RoleAdmin      Role = "admin"
	RoleModerator  Role = "moderator"
	RoleDeveloper  Role = "developer"
	RoleUser       Role = "user"
)

Server roles

type ServerInfoHandler

type ServerInfoHandler struct {
	// ServerName is the current name of the server. This
	// should be the same as the domain used to access it.
	ServerName string
	// PreviousNames should contain any previous names this server used.
	PreviousNames []string

	// PublicKey should contain the server's public Ed25519 key.
	PublicKey ed25519.PublicKey
	// PrivateKey should contain the server's private Ed25519 key.
	PrivateKey ed25519.PrivateKey
	// PreviousKeys should contain any previously-used private keys.
	// If this is not provided when the key changes, servers will not
	// trust the new key and all responses will be rejected.
	PreviousKeys []ed25519.PrivateKey

	// ErrorHandler is called whenever an error is encountered.
	ErrorHandler func(err error, res http.ResponseWriter)
}

ServerInfoHandler handles the server info endpoint defined by ProfileFed.

func (ServerInfoHandler) ServeHTTP

func (sih ServerInfoHandler) ServeHTTP(res http.ResponseWriter, req *http.Request)

ServeHTTP implements the http.Handler interface

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL