npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@localfirst/auth-provider-automerge-repo

v6.0.0

Published

Authentication provider for automerge-repo using localfirst/auth

Downloads

141

Readme

Local-first auth provider for Automerge Repo

This is an AuthProvider that uses the localfirst/auth library to provide Automerge Repo with authentication and end-to-end encryption, without the need for a central server.

It works by wrapping the network provider(s) used by the Repo.

Making an authenticated connection

A AuthProvider is configured with information about the local user and device.

import { createUser, createDevice } from '@localfirst/auth'
import { AuthProvider } from '@localfirst/auth-provider-automerge-repo'

// Create the user & device, or retrieve them from storage.
// These objects include secret keys, so need to be stored securely.
const user = createUser('alice')
const device = createDevice(alice.userId, 'ALICE-MACBOOK-2023')

// Use the same storage adapter for the `AuthProvider` and the `Repo.`
const storage = new SomeStorageAdapter()

// Instantiate the auth provider.
const authProvider = new AuthProvider({ user, device, storage })

// Use it to wrap your network adapter.
const adapter = new SomeNetworkAdapter()
const network = [authProvider.wrap(adapter)]

// Instantiate the repo.
const repo = new Repo({ network, storage })

The context for authentication is a localfirst/auth team. For example, Alice might create a team and invite Bob to it.

// Alice creates a team
const team = authProvider.createTeam('team A')

// (this is a shorthand for creating the team yourself and adding it to the team)
// const team = Auth.createTeam('team A', aliceContext)
// authProvider.addTeam(team)

// Alice creates an invitation code to send to Bob
const { seed: bobInviteCode } = team.inviteMember()

Alice now needs to communicate this code, along with the team's ID, to Bob, using an existing communications channel that she trusts. For example, she could send it via WhatsApp or Signal or email; or she could create a QR code for Bob to scan; or she could read it to him over the phone.

Bob sets up his auth provider and his repo much like Alice did:

const user = createUser('bob')
const device = createDevice(bob.userId, 'BOB-IPHONE-2023')
const storage = new SomeStorageAdapter()
const authProvider = new AuthProvider({ user, device, storage })
const adapter = new SomeNetworkAdapter()
const network = [authProvider.wrap(adapter)]
const repo = new Repo({ network, storage })

Bob registers his invitation with the team:

bobAuthProvider.addInvitation({
  shareId: aliceTeam.id,
  invitationSeed: bobInviteCode,
})

If all goes well, Alice's repo and Bob's repo will each receive a peer event, just like without the auth provider — but with an authenticated peer on the other end, and an encrypted channel for communication.

Here's how that works under the hood:

  • The AuthProvider wraps the network adapter so it can intercept its messages and events.
  • We intercept the adapter's peer-candidate event, and before surfacing it to the repo we run the localfirst/auth connection protocol over that channel.
  • In this case, Bob sends Alice cryptographic proof that he has the invitation code; and Alice can use that proof to validate his invitation and admit him to the team. He gives her his public keys, which she records on the team.
  • Alice then sends him the team's serialized graph, so he has a complete copy. He can use this to verify that this is in fact the team he was invited to, and to obtain Alice's public keys.
  • Alice and Bob use each other's public keys to exchange asymmetrically encrypted seed information and agree on a session key, which they begin using to symmetrically encrypt further communication.
  • Once that is done, the authenticated network adapter re-emits the peer-candidate event to the network subsystem.

The repo can then go about its business of synchronizing documents, but with the assurance that every peer ID reported by the network has been authenticated, and that all traffic is also authenticated and safe from eavesdropping.

Authenticated sync server

For this to work with a sync server in a star-shaped network, the sync server needs to use the auth provider as well. For that we have @localfirst/auth-syncserver, a drop-in replacement for the Automerge Repo sync server.

When using the auth provider with a sync server, provide the server's hostname when instantiating:

const authProvider = new AuthProvider({
  user,
  device,
  storage,
  server: 'localhost:3030',
})

If you use multiple sync servers, you can provide an array:

const authProvider = new AuthProvider({
  user,
  device,
  storage,
  server: ['sync1.example.com', 'sync2.example.com'],
})

Alternatively, you can add servers to an existing provider:

authProvider.addServer('sync3.example.com')

When you use the auth provider to create a team, it will automatically register the new team with the server.

const team = authProvider.createTeam('team A')