131 lines
3.4 KiB
TypeScript
131 lines
3.4 KiB
TypeScript
import { Turnkey, type TurnkeyApiTypes } from '@turnkey/sdk-server'
|
|
|
|
// Default path for the first Ethereum address in a new HD wallet.
|
|
// See https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki, paths are in the form:
|
|
// m / purpose' / coin_type' / account' / change / address_index
|
|
// - Purpose is a constant set to 44' following the BIP43 recommendation.
|
|
// - Coin type is set to 60 (ETH) -- see https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
|
// - Account, Change, and Address Index are set to 0
|
|
import { DEFAULT_ETHEREUM_ACCOUNTS } from '@turnkey/sdk-server'
|
|
import type { Service } from './service'
|
|
import { getConfig } from './utils'
|
|
|
|
type TAttestation = TurnkeyApiTypes['v1Attestation']
|
|
|
|
type CreateUserParams = {
|
|
userName: string
|
|
userEmail: string
|
|
challenge: string
|
|
attestation: TAttestation
|
|
}
|
|
|
|
export async function createUser(
|
|
service: Service,
|
|
{ userName, userEmail, challenge, attestation }: CreateUserParams
|
|
) {
|
|
try {
|
|
if (await service.getUserByEmail(userEmail)) {
|
|
throw new Error(`User already exists: ${userEmail}`)
|
|
}
|
|
|
|
const config = await getConfig()
|
|
const turnkey = new Turnkey(config.turnkey)
|
|
|
|
const apiClient = turnkey.api()
|
|
|
|
const walletName = 'Default ETH Wallet'
|
|
|
|
const createSubOrgResponse = await apiClient.createSubOrganization({
|
|
subOrganizationName: `Default SubOrg for ${userEmail}`,
|
|
rootQuorumThreshold: 1,
|
|
rootUsers: [
|
|
{
|
|
userName,
|
|
userEmail,
|
|
apiKeys: [],
|
|
authenticators: [
|
|
{
|
|
authenticatorName: 'Passkey',
|
|
challenge,
|
|
attestation
|
|
}
|
|
]
|
|
}
|
|
],
|
|
wallet: {
|
|
walletName: walletName,
|
|
accounts: DEFAULT_ETHEREUM_ACCOUNTS
|
|
}
|
|
})
|
|
|
|
const subOrgId = refineNonNull(createSubOrgResponse.subOrganizationId)
|
|
const wallet = refineNonNull(createSubOrgResponse.wallet)
|
|
|
|
const result = {
|
|
id: wallet.walletId,
|
|
address: wallet.addresses[0],
|
|
subOrgId: subOrgId
|
|
}
|
|
console.log('Turnkey success', result)
|
|
|
|
const user = await service.createUser({
|
|
name: userName,
|
|
email: userEmail,
|
|
subOrgId,
|
|
ethAddress: wallet.addresses[0],
|
|
turnkeyWalletId: wallet.walletId
|
|
})
|
|
console.log('New user', user)
|
|
|
|
return user
|
|
} catch (e) {
|
|
console.error('Failed to create user:', e)
|
|
throw e
|
|
}
|
|
}
|
|
|
|
export async function authenticateUser(
|
|
service: Service,
|
|
signedWhoamiRequest: {
|
|
url: string
|
|
body: any
|
|
stamp: {
|
|
stampHeaderName: string
|
|
stampHeaderValue: string
|
|
}
|
|
}
|
|
) {
|
|
try {
|
|
const tkRes = await fetch(signedWhoamiRequest.url, {
|
|
method: 'POST',
|
|
body: signedWhoamiRequest.body,
|
|
headers: {
|
|
[signedWhoamiRequest.stamp.stampHeaderName]:
|
|
signedWhoamiRequest.stamp.stampHeaderValue
|
|
}
|
|
})
|
|
console.log('AUTH RESULT', tkRes.status)
|
|
if (tkRes.status !== 200) {
|
|
console.log(await tkRes.text())
|
|
return null
|
|
}
|
|
const orgId = (await tkRes.json()).organizationId
|
|
const user = await service.getUserBySubOrgId(orgId)
|
|
return user
|
|
} catch (e) {
|
|
console.error('Failed to authenticate:', e)
|
|
throw e
|
|
}
|
|
}
|
|
|
|
function refineNonNull<T>(
|
|
input: T | null | undefined,
|
|
errorMessage?: string
|
|
): T {
|
|
if (input == null) {
|
|
throw new Error(errorMessage ?? `Unexpected ${JSON.stringify(input)}`)
|
|
}
|
|
|
|
return input
|
|
}
|