Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App owned: account linking #335

Merged
merged 77 commits into from
Mar 17, 2022
Merged
Show file tree
Hide file tree
Changes from 65 commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
77882e0
wip: linking code
walkah Dec 10, 2021
f296aa5
wip
walkah Dec 15, 2021
0f9a6dd
wip
walkah Jan 7, 2022
53e3cb9
adding some comments for clarification
walkah Jan 7, 2022
dba6add
Add producer and consumer modules and pin challenge
bgins Jan 14, 2022
90d03dd
wip: Add account delegation and device linking
bgins Jan 17, 2022
7f9ac13
Update user challenge naming and type
bgins Jan 17, 2022
db15bd9
Add consumer onCompletion callback
bgins Jan 18, 2022
a594584
Add start config types and fine tune onCompletion
bgins Jan 19, 2022
b64d449
Change delegateAccount and linkDevice types
bgins Jan 19, 2022
478fd54
Remove initial implementation
bgins Jan 19, 2022
16174d9
Add username to producer onCompletion
bgins Jan 20, 2022
cec24f8
Convert internal maybes to Maybe
bgins Jan 20, 2022
85003f5
Convert callback interface to event emitter
bgins Jan 27, 2022
4b83b3e
Add account linking declined message
bgins Jan 31, 2022
d1edcf8
Refactor to remove module global state and more
bgins Feb 2, 2022
7bdc831
Short circuit dispatch when no listeners
bgins Feb 2, 2022
9fd5e5f
Add error handling
bgins Feb 9, 2022
56f2ace
Remove unused channel receive function
bgins Feb 9, 2022
8b1d7c7
Add missing .js extensions to imports
bgins Feb 9, 2022
20e2d13
Fix typo in error message
bgins Feb 9, 2022
9e59d0d
Decouple linking functions from linking state
bgins Feb 10, 2022
76fd62a
Export linking functions for testing
bgins Feb 10, 2022
3af3e1d
Add unit tests and prop checks
bgins Feb 14, 2022
4432000
Clean up comments
bgins Feb 14, 2022
4fdbdf3
Add producer state transition to delegation
bgins Feb 14, 2022
d899606
Add consumer temporary key exchange retry
bgins Feb 15, 2022
a82f2ef
Add consumer broadcast state warning
bgins Feb 15, 2022
8e9f50d
Add producer warning for spurious DID messages
bgins Feb 15, 2022
85f318e
Add producer check for stored username
bgins Feb 15, 2022
78dca18
Remove unneeded linking state reset
bgins Feb 16, 2022
da563cb
Add received message while delegating warning
bgins Feb 16, 2022
932bf49
Add producer linking preflight
bgins Feb 16, 2022
4f12186
Add websocket data type
bgins Feb 16, 2022
6476724
Fix message decoding calls
bgins Feb 17, 2022
3cf35a9
Dependency inject checkCapability
bgins Feb 17, 2022
a5d0df9
Remove console logs and unneeded comments
bgins Feb 17, 2022
ce31a2e
Change linkDevice signature to return void
bgins Feb 17, 2022
59d64e7
Add username param to delegateAccount
bgins Feb 18, 2022
12226a1
Add initial integration test
bgins Feb 19, 2022
f77d4d1
Print linking warnings when debug enabled
bgins Feb 23, 2022
144fa93
Clean up
bgins Feb 23, 2022
fd99db5
Re-export producer and consumer
bgins Feb 24, 2022
81d90e6
Convert confirm and reject pin to call delegation
bgins Feb 24, 2022
11ca2b0
Add tryParseMessage guard
bgins Feb 24, 2022
dc4e055
Add more integration tests
bgins Feb 24, 2022
094fa63
Simplify channel DID lookup
bgins Feb 25, 2022
aa2d8a6
Re-export account linking functions from top level
bgins Feb 25, 2022
31c90dd
Fix consumer closes early issue
bgins Feb 25, 2022
00d51f3
Add linkDevice unit tests
bgins Feb 25, 2022
4bf7037
Add delegateAccount and declineDelegation unit tests
bgins Feb 25, 2022
4895c4f
Fix declineDelegation approval message
bgins Feb 25, 2022
9f4f121
Clean up post-rebase
bgins Feb 28, 2022
b0d61bb
Remove cyclic imports
bgins Feb 28, 2022
2e8da7a
Rename and export provider and requestor types
bgins Feb 28, 2022
4472cfb
Add auth lobby DI implementation
bgins Feb 28, 2022
89cede9
Add docstrings to external calls
bgins Mar 1, 2022
c2372a4
Improve comments
bgins Mar 1, 2022
dcb1697
Make error message more consistent
bgins Mar 1, 2022
14a47d9
Mark multiple consumer test as skipped
bgins Mar 1, 2022
5ed2dee
Update changelog
bgins Mar 1, 2022
98ceb8f
Update version
bgins Mar 1, 2022
680b298
Remove unused dependencies file
bgins Mar 1, 2022
f75f4d2
Rename WebSocketData to ChannelData
bgins Mar 2, 2022
e61d9a9
Add once guard to confirm and reject pin
bgins Mar 2, 2022
2ab2844
Remove underscore variable
bgins Mar 3, 2022
6464460
Rename createChannel to createWssChannel
bgins Mar 3, 2022
a2cb14a
Decrease number of filesystem API test runs
bgins Mar 3, 2022
787db78
Move LinkingStep type to common linking module
bgins Mar 3, 2022
59287f7
Fix account registration exports
bgins Mar 14, 2022
879ef37
Remove console logs
bgins Mar 14, 2022
38327f2
Rename provider and requestor
bgins Mar 14, 2022
4ce643b
Convert linking steps to an enum
bgins Mar 14, 2022
2400efd
Remove unused temporaryRsaPair from producer
bgins Mar 14, 2022
2470765
Use event maps (typed EventEmitter)
matheus23 Mar 15, 2022
bff0d57
Convert event emitter to node style interface
bgins Mar 15, 2022
43fb3f1
Remove ineffecual socket set to null
bgins Mar 16, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Changelog


### v0.32.0

- Adds app owned account linking

### v0.31.1

Move `madge` and `typedoc-plugin-missing-exports` from `dependencies` into `devDependencies`.
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": "webnative",
"version": "0.31.1",
"version": "0.32.0",
"description": "Fission Webnative SDK",
"keywords": [
"WebCrypto",
Expand Down
73 changes: 73 additions & 0 deletions src/auth/channel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import * as did from "../did/index.js"
import { setup } from "../setup/internal.js"
import { LinkingError } from "./linking.js"

import type { Maybe } from "../common/index.js"

export type Channel = {
send: (data: ChannelData) => void
close: () => void
}

export type ChannelOptions = {
username: string
handleMessage: (event: MessageEvent) => void
}

type ChannelData = string | ArrayBufferLike | Blob | ArrayBufferView

export const createChannel = async (options: ChannelOptions): Promise<Channel> => {
bgins marked this conversation as resolved.
Show resolved Hide resolved
const { username, handleMessage } = options

const rootDid = await await did.root(username).catch(_ => null)
bgins marked this conversation as resolved.
Show resolved Hide resolved
if (!rootDid) {
throw new LinkingError(`Failed to lookup DID for ${username}`)
}

const apiEndpoint = setup.getApiEndpoint()
const endpoint = apiEndpoint.replace(/^https?:\/\//, "wss://")
bgins marked this conversation as resolved.
Show resolved Hide resolved
const topic = `deviceLink#${rootDid}`
console.log("Opening channel", topic)

const socket: Maybe<WebSocket> = new WebSocket(`${endpoint}/user/link/${rootDid}`)
await waitForOpenConnection(socket)
socket.onmessage = handleMessage

const send = publishOnWssChannel(socket)
const close = closeWssChannel(socket)

return {
send,
close
}
}

const waitForOpenConnection = async (socket: WebSocket): Promise<void> => {
return new Promise((resolve, reject) => {
socket.onopen = () => {
resolve()
}
socket.onerror = () => {
reject("Websocket channel could not be opened")
}
})
}

export const closeWssChannel = (socket: Maybe<WebSocket>): () => void => {
return function () {
if (socket) {
socket.close(1000)
}
socket = null
}
}

export const publishOnWssChannel = (socket: WebSocket): (data: ChannelData) => void => {
return function (data: ChannelData) {
const binary = typeof data === "string"
? new TextEncoder().encode(data).buffer
: data

socket?.send(binary)
}
}
5 changes: 5 additions & 0 deletions src/auth/implementation/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { InitOptions } from "../../init/types.js"
import { State } from "../state.js"

import type { Channel, ChannelOptions } from "../../auth/channel"

export type Implementation = {
init: (options: InitOptions) => Promise<State | null>
register: (options: { email: string; username: string }) => Promise<{ success: boolean }>
isUsernameValid: (username: string) => Promise<boolean>
isUsernameAvailable: (username: string) => Promise<boolean>
createChannel: (options: ChannelOptions) => Promise<Channel>
checkCapability: (username: string) => Promise<boolean>
delegateAccount: (username: string, audience: string) => Promise<Record<string, unknown>>
linkDevice: (data: Record<string, unknown>) => Promise<void>
}
7 changes: 7 additions & 0 deletions src/auth/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { impl } from "./implementation.js"

const { isUsernameAvailable, isUsernameValid, register } = impl

export { isUsernameAvailable, isUsernameValid, register }
export { AccountLinkingRequestor, createConsumer as createRequestor } from "./linking/consumer.js"
export { AccountLinkingProvider, createProducer as createProvider } from "./linking/producer.js"
bgins marked this conversation as resolved.
Show resolved Hide resolved
21 changes: 20 additions & 1 deletion src/auth/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { InitOptions } from "../init/types.js"
import { State } from "./state.js"


import type { Channel, ChannelOptions } from "./channel"

export const init = (options: InitOptions): Promise<State | null> => {
return authLobby.init(options)
}

export const register = (options: { username: string; email: string }): Promise<{success: boolean}> => {
export const register = (options: { username: string; email: string }): Promise<{ success: boolean }> => {
return authLobby.register(options)
}

Expand All @@ -18,3 +20,20 @@ export const isUsernameValid = (username: string): Promise<boolean> => {
export const isUsernameAvailable = (username: string): Promise<boolean> => {
return authLobby.isUsernameAvailable(username)
}


export const createChannel = (options: ChannelOptions): Promise<Channel> => {
return authLobby.createChannel(options)
}

export const checkCapability = async (username: string): Promise<boolean> => {
return authLobby.checkCapability(username)
}

export const delegateAccount = (username: string, audience: string): Promise<Record<string, unknown>> => {
return authLobby.delegateAccount(username, audience)
}

export const linkDevice = (data: Record<string, unknown>): Promise<void> => {
return authLobby.linkDevice(data)
}
60 changes: 60 additions & 0 deletions src/auth/linking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type { Result } from "../common/index.js"

import * as debug from "../common/debug.js"


export class LinkingError extends Error {
constructor(message: string) {
super(message)
this.name = "LinkingError"
}
}

export class LinkingWarning extends Error {
constructor(message: string) {
super(message)
this.name = "LinkingWarning"
}
}

export const handleLinkingError = (error: LinkingError | LinkingWarning): void => {
switch (error.name) {
case "LinkingWarning":
debug.warn(error.message)
break

case "LinkingError":
throw error

default:
throw error
}
}

export const tryParseMessage = <T>(
data: string,
typeGuard: (message: unknown) => message is T,
context: { participant: string; callSite: string }
): Result<T, LinkingWarning> => {
try {
const message = JSON.parse(data)

if (typeGuard(message)) {
return {
ok: true,
value: message
}
} else {
return {
ok: false,
error: new LinkingWarning(`${context.participant} received an unexpected message in ${context.callSite}: ${data}. Ignoring message.`)
}
}

} catch {
return {
ok: false,
error: new LinkingWarning(`${context.participant} received a message in ${context.callSite} that it could not parse: ${data}. Ignoring message.`)
}
}
}
Loading