Implement payments for app deployments #17
@ -57,4 +57,10 @@ export interface Config {
|
|||||||
gitHub: GitHubConfig;
|
gitHub: GitHubConfig;
|
||||||
registryConfig: RegistryConfig;
|
registryConfig: RegistryConfig;
|
||||||
auction: AuctionConfig;
|
auction: AuctionConfig;
|
||||||
|
turnkey: {
|
||||||
|
apiBaseUrl: string;
|
||||||
|
apiPublicKey: string;
|
||||||
|
apiPrivateKey: string;
|
||||||
|
defaultOrganizationId: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,50 +1,50 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import { SiweMessage } from 'siwe';
|
import { SiweMessage } from 'siwe';
|
||||||
import { Service } from '../service';
|
import { Service } from '../service';
|
||||||
// import { authenticateUser, createUser } from '../turnkey-backend';
|
import { authenticateUser, createUser } from '../turnkey-backend';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Turnkey
|
// Turnkey
|
||||||
//
|
//
|
||||||
// router.get('/registration/:email', async (req, res) => {
|
router.get('/registration/:email', async (req, res) => {
|
||||||
// const service: Service = req.app.get('service');
|
const service: Service = req.app.get('service');
|
||||||
// const user = await service.getUserByEmail(req.params.email);
|
const user = await service.getUserByEmail(req.params.email);
|
||||||
// if (user) {
|
if (user) {
|
||||||
// return res.send({ subOrganizationId: user?.subOrgId });
|
return res.send({ subOrganizationId: user?.subOrgId });
|
||||||
// } else {
|
} else {
|
||||||
// return res.sendStatus(204);
|
return res.sendStatus(204);
|
||||||
// }
|
}
|
||||||
// });
|
});
|
||||||
|
|
||||||
// router.post('/register', async (req, res) => {
|
router.post('/register', async (req, res) => {
|
||||||
// console.log('Register', req.body);
|
console.log('Register', req.body);
|
||||||
// const { email, challenge, attestation } = req.body;
|
const { email, challenge, attestation } = req.body;
|
||||||
// const user = await createUser(req.app.get('service'), {
|
const user = await createUser(req.app.get('service'), {
|
||||||
// challenge,
|
challenge,
|
||||||
// attestation,
|
attestation,
|
||||||
// userEmail: email,
|
userEmail: email,
|
||||||
// userName: email.split('@')[0],
|
userName: email.split('@')[0],
|
||||||
// });
|
});
|
||||||
// req.session.address = user.id;
|
req.session.address = user.id;
|
||||||
// res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
// });
|
});
|
||||||
|
|
||||||
// router.post('/authenticate', async (req, res) => {
|
router.post('/authenticate', async (req, res) => {
|
||||||
// console.log('Authenticate', req.body);
|
console.log('Authenticate', req.body);
|
||||||
// const { signedWhoamiRequest } = req.body;
|
const { signedWhoamiRequest } = req.body;
|
||||||
// const user = await authenticateUser(
|
const user = await authenticateUser(
|
||||||
// req.app.get('service'),
|
req.app.get('service'),
|
||||||
// signedWhoamiRequest,
|
signedWhoamiRequest,
|
||||||
// );
|
);
|
||||||
// if (user) {
|
if (user) {
|
||||||
// req.session.address = user.id;
|
req.session.address = user.id;
|
||||||
// res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
// } else {
|
} else {
|
||||||
// res.sendStatus(401);
|
res.sendStatus(401);
|
||||||
// }
|
}
|
||||||
// });
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
// SIWE Auth
|
// SIWE Auth
|
||||||
|
130
packages/backend/src/turnkey-backend.ts
Normal file
130
packages/backend/src/turnkey-backend.ts
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
import { Turnkey, 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 { getConfig } from './utils';
|
||||||
|
import { Service } from './service';
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user