edge-core-js
Version:
Edge account & wallet management library
155 lines (130 loc) • 4.34 kB
JavaScript
function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }import { wasCreateLoginPayload } from '../../types/server-cleaners'
import {
asMaybeUsernameError,
} from '../../types/types'
import { encrypt } from '../../util/crypto/crypto'
import { makeKeysKit } from './keys'
import { loginFetch } from './login-fetch'
import { makeSecretKit } from './login-secret'
import { hashUsername } from './login-selectors'
import { saveStash } from './login-stash'
import { makeUsernameKit } from './login-username'
import { makePasswordKit } from './password'
import { makeChangePin2Kit } from './pin2'
/**
* Determines whether or not a username is available.
*/
export async function usernameAvailable(
ai,
username,
challengeId
) {
const userId = await hashUsername(ai, username)
const request = {
challengeId,
userId
}
return await loginFetch(ai, 'POST', '/v2/login', request)
.then(reply => false) // It's not available if we can hit it!
.catch((error) => {
if (asMaybeUsernameError(error) != null) return true
throw error
})
}
/**
* Assembles all the data needed to create a new login.
*/
export async function makeCreateKit(
ai,
parentSessionKey,
appId,
opts
) {
const { keyInfo, password, pin, username } = opts
const { io } = ai.props
// For crash errors:
ai.props.log.breadcrumb('makeCreateKit', {})
// Figure out login identity:
const isRoot = parentSessionKey == null
const loginId = io.random(32)
const loginKey = io.random(32)
const sessionKey = { loginId, loginKey }
// Create the basic login object, but without any authentication methods:
const login = {
appId,
lastLogin: new Date(),
loginId,
loginKey,
isRoot,
pendingVouchers: [],
children: []
}
const secretKit = makeSecretKit(ai, login)
let keysKit
let parentBox
let passwordKit
let pin2Kit
let usernameKit
// Set up optional login methods:
if (keyInfo != null) {
keysKit = makeKeysKit(ai, sessionKey, [keyInfo])
}
if (parentSessionKey != null) {
parentBox = encrypt(io, loginKey, parentSessionKey.loginKey)
}
if (password != null && username != null) {
passwordKit = await makePasswordKit(ai, login, username, password)
}
if (pin != null) {
pin2Kit = makeChangePin2Kit(ai, login, username, pin, true)
}
if (isRoot && username != null) {
usernameKit = await makeUsernameKit(ai, login, username)
}
// Bundle everything:
const kit = {
loginId,
server: {
...wasCreateLoginPayload({
appId,
loginId,
parentBox
}),
..._optionalChain([keysKit, 'optionalAccess', _ => _.server]),
..._optionalChain([passwordKit, 'optionalAccess', _2 => _2.server]),
..._optionalChain([pin2Kit, 'optionalAccess', _3 => _3.server]),
...secretKit.server,
..._optionalChain([usernameKit, 'optionalAccess', _4 => _4.server])
},
serverPath: '/v2/login/create',
stash: {
appId,
loginId,
parentBox,
..._optionalChain([keysKit, 'optionalAccess', _5 => _5.stash]),
..._optionalChain([passwordKit, 'optionalAccess', _6 => _6.stash]),
..._optionalChain([pin2Kit, 'optionalAccess', _7 => _7.stash]),
...secretKit.stash,
..._optionalChain([usernameKit, 'optionalAccess', _8 => _8.stash])
}
}
return { kit, sessionKey }
}
/**
* Creates a new login on the auth server.
*/
export async function createLogin(
ai,
accountOpts,
opts
) {
const { challengeId, now = new Date() } = accountOpts
// For crash errors:
ai.props.log.breadcrumb('createLogin', {})
const { kit, sessionKey } = await makeCreateKit(ai, undefined, '', opts)
const request = { challengeId, data: kit.server }
await loginFetch(ai, 'POST', kit.serverPath, request)
kit.stash.lastLogin = now
await saveStash(ai, kit.stash )
return sessionKey
}