Skip to content
Open
2 changes: 1 addition & 1 deletion demos/taco-chat/src/hooks/useTeam.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { type AlertInfo, type PeerState } from '../types.js'
import { assert } from '@localfirst/shared'
import { ConnectionManager } from 'ConnectionManager.js'
import { teamContext } from 'components/TeamProvider.js'
import { randomTeamName } from 'util/randomTeamName.js'
import { randomTeamName } from '../util/randomTeamName.js'

// TODO: make this an environment var
const relayUrls = ['ws://localhost:8080']
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@localfirst/auth-monorepo",
"version": "6.0.0",
"version": "6.0.1",
"private": true,
"description": "Monorepo for @localfirst/auth",
"repository": "http://github.com/local-first-web/auth",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { type Message } from '@automerge/automerge-repo'
import { debug } from '@localfirst/shared'
import { AbstractConnection, type StateValue } from 'AbstractConnection.js'
import { AbstractConnection, type StateValue } from './AbstractConnection.js'
import { pack, unpack } from 'msgpackr'
import { type ShareId } from 'types.js'
import { type ShareId } from './types.js'

export class AnonymousConnection extends AbstractConnection {
readonly #log: debug.Debugger
Expand Down
25 changes: 16 additions & 9 deletions packages/auth-provider-automerge-repo/src/AuthProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import type {
import { EventEmitter } from '@herbcaudill/eventemitter42'
import * as Auth from '@localfirst/auth'
import { hash } from '@localfirst/crypto'
import { debug, memoize, pause } from '@localfirst/shared'
import { type AbstractConnection } from 'AbstractConnection.js'
import { AnonymousConnection } from 'AnonymousConnection.js'
import { buildServerUrl } from 'buildServerUrl.js'
import { getShareId } from 'getShareId.js'
import { assert, debug, memoize, pause } from '@localfirst/shared'
import { type AbstractConnection } from './AbstractConnection.js'
import { AnonymousConnection } from './AnonymousConnection.js'
import { buildServerUrl } from './buildServerUrl.js'
import { getShareId } from './getShareId.js'
import { pack, unpack } from 'msgpackr'
import { isJoinMessage, type JoinMessage } from 'types.js'
import { isJoinMessage, type JoinMessage } from './types.js'
import { AuthenticatedNetworkAdapter as AuthNetworkAdapter } from './AuthenticatedNetworkAdapter.js'
import { CompositeMap } from './CompositeMap.js'
import type {
Expand Down Expand Up @@ -211,7 +211,9 @@ export class AuthProvider extends EventEmitter<AuthProviderEvents> {
* Creates a team and registers it with all of our sync servers.
*/
public async createTeam(teamName: string) {
const team = await Auth.createTeam(teamName, {
assert(this.#user, 'Cannot create team as user is missing on AuthProvider')

const team = Auth.createTeam(teamName, {
device: this.#device,
user: this.#user,
})
Expand Down Expand Up @@ -661,6 +663,8 @@ export class AuthProvider extends EventEmitter<AuthProviderEvents> {

const savedShares = unpack(serializedState) as SerializedState

assert(this.#user, 'Cannot load state as user is missing on AuthProvider')

await Promise.all(
Object.values(savedShares).map(async share => {
if ('encryptedTeam' in share) {
Expand All @@ -672,9 +676,12 @@ export class AuthProvider extends EventEmitter<AuthProviderEvents> {
this.#device.keys.secretKey
) as Auth.KeysetWithSecrets

const context = { device: this.#device, user: this.#user }
// By this point it is defined
assert(this.#user)

const context: Auth.LocalContext = { device: this.#device, user: this.#user }

const team = await Auth.loadTeam(encryptedTeam, context, teamKeys)
const team = Auth.loadTeam(encryptedTeam, context, teamKeys)
return this.addTeam(team)
} else {
return this.joinPublicShare(share.shareId)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NetworkAdapter, type Message } from '@automerge/automerge-repo'
import { type ShareId } from 'types.js'
import { type ShareId } from './types.js'

/**
* An AuthenticatedNetworkAdapter is a NetworkAdapter that wraps another NetworkAdapter and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { MessageChannelNetworkAdapter } from '@automerge/automerge-repo-network-
import { NodeFSStorageAdapter } from '@automerge/automerge-repo-storage-nodefs'
import * as Auth from '@localfirst/auth'
import { eventPromise } from '@localfirst/shared'
import { getShareId } from 'getShareId.js'
import { type ShareId } from 'types.js'
import { getShareId } from '../getShareId.js'
import { type ShareId } from '../types.js'
import { describe, expect, it } from 'vitest'
import { AuthProvider } from '../AuthProvider.js'
import { authenticated, authenticatedInTime } from './helpers/authenticated.js'
Expand Down
7 changes: 6 additions & 1 deletion packages/auth-provider-automerge-repo/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,12 @@ export type AuthProviderEvents = {
* this is how we get the user's keys.) This event gives the application a chance to persist the
* team graph and the user's info.
*/
joined: (payload: { shareId: ShareId; peerId: PeerId; team: Auth.Team; user: Auth.User }) => void
joined: (payload: {
shareId: ShareId
peerId: PeerId
team: Auth.Team
user: Auth.UserWithSecrets
}) => void

/**
* We're connected to a peer and have been mutually authenticated.
Expand Down
10 changes: 3 additions & 7 deletions packages/auth-provider-automerge-repo/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": "src",
"outDir": "dist",
"rootDir": "src",
"rootDir": "src"
},
"include": [
"src",
"@types"
]
}
"include": ["src", "@types"]
}
10 changes: 3 additions & 7 deletions packages/auth-syncserver/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": "src",
"outDir": "dist",
"rootDir": "src",
"rootDir": "src"
},
"include": [
"src",
"@types"
]
}
"include": ["src", "@types"]
}
40 changes: 23 additions & 17 deletions packages/auth/src/connection/Connection.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable object-shorthand */
import { EventEmitter } from '@herbcaudill/eventemitter42'
import type { DecryptFnParams } from '@localfirst/crdx'
import type { DecryptFn, DecryptFnParams, Graph } from '@localfirst/crdx'
import {
generateMessage,
headsAreEqual,
Expand All @@ -10,7 +10,7 @@ import {
} from '@localfirst/crdx'
import { asymmetric, base58, randomKeyBytes, symmetric, type Hash } from '@localfirst/crypto'
import { assert, debug } from '@localfirst/shared'
import { deriveSharedKey } from 'connection/deriveSharedKey.js'
import { deriveSharedKey } from './deriveSharedKey.js'
import {
DEVICE_REMOVED,
DEVICE_UNKNOWN,
Expand All @@ -25,19 +25,25 @@ import {
UNHANDLED,
createErrorMessage,
type ConnectionErrorType,
} from 'connection/errors.js'
import { getDeviceUserFromGraph } from 'connection/getDeviceUserFromGraph.js'
import * as identity from 'connection/identity.js'
import type { ConnectionMessage, DisconnectMessage } from 'connection/message.js'
import { redactDevice } from 'device/index.js'
import * as invitations from 'invitation/index.js'
} from './errors.js'
import { getDeviceUserFromGraph } from './getDeviceUserFromGraph.js'
import * as identity from './identity.js'
import type { ConnectionMessage, DisconnectMessage } from './message.js'
import { redactDevice } from '../device/index.js'
import * as invitations from '../invitation/index.js'
import { pack, unpack } from 'msgpackr'
import { getTeamState } from 'team/getTeamState.js'
import { Team, decryptTeamGraph, type TeamAction, type TeamContext } from 'team/index.js'
import * as select from 'team/selectors/index.js'
import { arraysAreEqual } from 'util/arraysAreEqual.js'
import { KeyType } from 'util/index.js'
import { syncMessageSummary } from 'util/testing/messageSummary.js'
import { getTeamState } from '../team/getTeamState.js'
import {
Team,
TeamGraph,
decryptTeamGraph,
type TeamAction,
type TeamContext,
} from '../team/index.js'
import * as select from '../team/selectors/index.js'
import { arraysAreEqual } from '../util/arraysAreEqual.js'
import { KeyType } from '../util/index.js'
import { syncMessageSummary } from '../util/testing/messageSummary.js'
import { and, assertEvent, assign, createActor, setup } from 'xstate'
import { MessageQueue, type NumberedMessage } from './MessageQueue.js'
import { extendServerContext, getUserName, messageSummary, stateSummary } from './helpers.js'
Expand All @@ -64,7 +70,7 @@ himself told me it's OK. So
https://github.com/statelyai/xstate/discussions/4783#discussioncomment-8673350

The bulk of this class is an XState state machine. It's instantiated in the constructor. It looks
something like this:
something like this:

```ts
const machine = setup({
Expand All @@ -80,7 +86,7 @@ const machine = setup({
```

To understand the way this flows, the best place to start is the state machine definition passed to
`createMachine`.
`createMachine`.

You can also visualize this machine in the Stately visualizer - here's a link that's current at time
of writing this:
Expand Down Expand Up @@ -308,7 +314,7 @@ export class Connection extends EventEmitter<ConnectionEvents> {
const deviceKeys = device.keys

// handle errors here
const decrypt = ({ encryptedGraph, keys }: DecryptFnParams<TeamAction, TeamContext>) =>
const decrypt: DecryptFn<TeamAction, TeamContext> = ({ encryptedGraph, keys }) =>
decryptTeamGraph({ encryptedGraph, teamKeys: keys, deviceKeys })

const [newChain, syncState] = receiveMessage(
Expand Down
2 changes: 1 addition & 1 deletion packages/auth/src/connection/deriveSharedKey.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { hash, hashBytes, type Base58, base58 } from '@localfirst/crypto'
import { HashPurpose } from 'util/index.js'
import { HashPurpose } from '../util/index.js'

/**
* Takes two seeds (in this case, provided by each of two peers that are connecting) and
Expand Down
11 changes: 7 additions & 4 deletions packages/auth/src/connection/getDeviceUserFromGraph.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { getLatestGeneration, type Keyring, type UserWithSecrets } from '@localfirst/crdx'
import { assert } from '@localfirst/shared'
import { generateProof } from 'invitation/generateProof.js'
import { generateStarterKeys } from 'invitation/generateStarterKeys.js'
import { KeyType } from 'util/index.js'
import { generateProof } from '../invitation/generateProof.js'
import { generateStarterKeys } from '../invitation/generateStarterKeys.js'
import { KeyType } from '../util/index.js'
import { getTeamState } from '../team/getTeamState.js'
import * as select from '../team/selectors/index.js'

Expand Down Expand Up @@ -42,7 +42,10 @@ export const getDeviceUserFromGraph = ({

const userKeyring = select.keyring(state, { type: USER, name: userId }, starterKeys)
const keys = getLatestGeneration(userKeyring)
const user = { userName, userId, keys }

assert(keys, 'Failed to get device user from graph as there are no keys for user keyring')

const user: UserWithSecrets = { userName, userId, keys }

return { user, userKeyring }
}
4 changes: 2 additions & 2 deletions packages/auth/src/connection/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ConnectionMessage } from 'connection/message.js'
import { syncMessageSummary } from 'util/testing/messageSummary.js'
import type { ConnectionMessage } from './message.js'
import { syncMessageSummary } from '../util/testing/messageSummary.js'
import type { Context, ServerContext } from './types.js'

// HELPERS
Expand Down
4 changes: 2 additions & 2 deletions packages/auth/src/connection/identity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {
type UnixTimestamp,
} from '@localfirst/crdx'
import { signatures, randomKey } from '@localfirst/crypto'
import { type Challenge } from 'connection/types.js'
import { VALID, type ValidationResult } from 'util/index.js'
import { type Challenge } from './types.js'
import { VALID, type ValidationResult } from '../util/index.js'

export const challenge = (identityClaim: KeyScope): Challenge => ({
...identityClaim,
Expand Down
2 changes: 1 addition & 1 deletion packages/auth/src/connection/message.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Base58, Hash, Keyring, SyncMessage as SyncPayload } from '@localfirst/crdx'
import type { Challenge, IdentityClaim } from 'connection/types.js'
import type { Challenge, IdentityClaim } from './types.js'
import type { ErrorMessage, LocalErrorMessage } from './errors.js'

export type ReadyMessage = {
Expand Down
8 changes: 4 additions & 4 deletions packages/auth/src/connection/test/authentication.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { eventPromise, pause } from '@localfirst/shared'
import { cloneDeep } from 'lodash-es'
import { ADMIN } from 'role/index.js'
import * as teams from 'team/index.js'
import { ADMIN } from '../../role/index.js'
import * as teams from '../../team/index.js'
import {
TestChannel,
all,
Expand All @@ -14,10 +14,10 @@ import {
joinTestChannel,
setup,
tryToConnect,
} from 'util/testing/index.js'
} from '../../util/testing/index.js'
import { describe, expect, it } from 'vitest'
import type { InviteeDeviceContext } from '../types.js'
import { createDevice } from 'device/createDevice.js'
import { createDevice } from '../../device/createDevice.js'

describe('connection', () => {
describe('authentication', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/auth/src/connection/test/connection.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TestChannel, all, connect, joinTestChannel, setup } from 'util/testing/index.js'
import { TestChannel, all, connect, joinTestChannel, setup } from '../../util/testing/index.js'
import { pause } from '@localfirst/shared'
import { describe, it } from 'vitest'

Expand Down
2 changes: 1 addition & 1 deletion packages/auth/src/connection/test/encryption.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest'
import { connect, setup } from 'util/testing/index.js'
import { connect, setup } from '../../util/testing/index.js'
import { eventPromise } from '@localfirst/shared'
import { randomKeyBytes } from '@localfirst/crypto'

Expand Down
8 changes: 4 additions & 4 deletions packages/auth/src/connection/test/identity.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { challenge, prove, verify } from 'connection/identity.js'
import { ADMIN_SCOPE, TEAM_SCOPE } from 'team/index.js'
import { setup } from 'util/testing/index.js'
import 'util/testing/expect/toBeValid.js'
import { challenge, prove, verify } from '../identity.js'
import { ADMIN_SCOPE, TEAM_SCOPE } from '../../team/index.js'
import { setup } from '../../util/testing/index.js'
import '../../util/testing/expect/toBeValid.js'
import { type KeyScope, KeyType, createKeyset, redactKeys } from '@localfirst/crdx'
import { describe, expect, it } from 'vitest'

Expand Down
4 changes: 2 additions & 2 deletions packages/auth/src/connection/test/sync.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ADMIN } from 'role/index.js'
import { ADMIN } from '../../role/index.js'
import {
TestChannel,
any,
Expand All @@ -13,7 +13,7 @@ import {
joinTestChannel,
setup,
updated,
} from 'util/testing/index.js'
} from '../../util/testing/index.js'
import { pause } from '@localfirst/shared'
import { describe, expect, it } from 'vitest'
import { type MemberContext } from '../types.js'
Expand Down
8 changes: 4 additions & 4 deletions packages/auth/src/connection/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ import type {
DeviceWithSecrets,
FirstUseDevice,
FirstUseDeviceWithSecrets,
} from 'device/index.js'
import type { ProofOfInvitation } from 'invitation/index.js'
import type { ServerWithSecrets } from 'server/index.js'
import type { Member, Team } from 'team/index.js'
} from '../device/index.js'
import type { ProofOfInvitation } from '../invitation/index.js'
import type { ServerWithSecrets } from '../server/index.js'
import type { Member, Team } from '../team/index.js'
import type { ConnectionErrorPayload } from './errors.js'
import type { ConnectionMessage } from './message.js'

Expand Down
2 changes: 1 addition & 1 deletion packages/auth/src/device/createDevice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createKeyset, type UnixTimestamp } from '@localfirst/crdx'
import { createId } from '@paralleldrive/cuid2'
import { randomKey } from '@localfirst/crypto'
import type { DeviceWithSecrets } from './types.js'
import { KeyType } from 'util/index.js'
import { KeyType } from '../util/index.js'

export const createDevice = ({
userId,
Expand Down
2 changes: 1 addition & 1 deletion packages/auth/src/device/redact.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { redactKeys } from '@localfirst/crdx'
import { type DeviceWithSecrets, type Device } from 'device/types.js'
import { type DeviceWithSecrets, type Device } from './types.js'

export const redactDevice = (device: DeviceWithSecrets): Device => ({
...device,
Expand Down
6 changes: 3 additions & 3 deletions packages/auth/src/invitation/create.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { type UnixTimestamp } from '@localfirst/crdx'
import { generateStarterKeys } from './generateStarterKeys.js'
import { deriveId } from 'invitation/deriveId.js'
import { normalize } from 'invitation/normalize.js'
import { type Invitation } from 'invitation/types.js'
import { deriveId } from './deriveId.js'
import { normalize } from './normalize.js'
import { type Invitation } from './types.js'

export const IKEY_LENGTH = 16

Expand Down
Loading