@devtion/devcli 0.0.0-57a8ab9 → 0.0.0-671e653
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.env +7 -0
- package/dist/index.js +281 -75
- package/dist/public/mini-semaphore.wasm +0 -0
- package/dist/public/mini-semaphore.zkey +0 -0
- package/dist/types/commands/authSIWE.d.ts +7 -0
- package/dist/types/commands/index.d.ts +1 -0
- package/dist/types/lib/localConfigs.d.ts +19 -0
- package/dist/types/types/index.d.ts +51 -0
- package/package.json +4 -2
- package/src/commands/auth.ts +7 -1
- package/src/commands/authBandada.ts +90 -69
- package/src/commands/authSIWE.ts +178 -0
- package/src/commands/contribute.ts +31 -17
- package/src/commands/index.ts +1 -0
- package/src/commands/logout.ts +2 -1
- package/src/index.ts +5 -0
- package/src/lib/localConfigs.ts +27 -0
- package/src/lib/services.ts +29 -17
- package/src/types/index.ts +55 -0
package/dist/.env
CHANGED
|
@@ -45,4 +45,11 @@ CONFIG_CEREMONY_BUCKET_POSTFIX=-p0tion-development-environment
|
|
|
45
45
|
# The amount of time in seconds which indicates the duration about the validity of a pre-signed URL.
|
|
46
46
|
# default: 7200 seconds = 2 hours.
|
|
47
47
|
CONFIG_PRESIGNED_URL_EXPIRATION_IN_SECONDS=7200
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# Sign In With Ethereum
|
|
51
|
+
# Auth0 client id
|
|
52
|
+
AUTH_SIWE_CLIENT_ID=tRuFnJNoPTJtKr1RynYfty6uJ16QzHXA
|
|
53
|
+
# The Auth0 application url that support SIWE + Device Flow Authentication
|
|
54
|
+
AUTH0_APPLICATION_URL=https://dev-l0tyk1agsmopw1xa.us.auth0.com
|
|
48
55
|
|
package/dist/index.js
CHANGED
|
@@ -238,6 +238,14 @@ const checkAndMakeNewDirectoryIfNonexistent = (directoryLocalPath) => {
|
|
|
238
238
|
const writeLocalJsonFile = (filePath, data) => {
|
|
239
239
|
fs.writeFileSync(filePath, JSON.stringify(data), "utf-8");
|
|
240
240
|
};
|
|
241
|
+
/**
|
|
242
|
+
* Return the local current project directory name.
|
|
243
|
+
* @returns <string> - the local project (e.g., dist/) directory name.
|
|
244
|
+
*/
|
|
245
|
+
const getLocalDirname = () => {
|
|
246
|
+
const filename = fileURLToPath(import.meta.url);
|
|
247
|
+
return path.dirname(filename);
|
|
248
|
+
};
|
|
241
249
|
|
|
242
250
|
// Get npm package name.
|
|
243
251
|
const packagePath$4 = `${dirname(fileURLToPath(import.meta.url))}/..`;
|
|
@@ -257,6 +265,10 @@ const config = new Conf({
|
|
|
257
265
|
bandadaIdentity: {
|
|
258
266
|
type: "string",
|
|
259
267
|
default: ""
|
|
268
|
+
},
|
|
269
|
+
authMethod: {
|
|
270
|
+
type: "string",
|
|
271
|
+
default: ""
|
|
260
272
|
}
|
|
261
273
|
}
|
|
262
274
|
});
|
|
@@ -336,6 +348,20 @@ const setLocalBandadaIdentity = (identity) => config.set("bandadaIdentity", iden
|
|
|
336
348
|
* Delete the stored Bandada identity.
|
|
337
349
|
*/
|
|
338
350
|
const deleteLocalBandadaIdentity = () => config.delete("bandadaIdentity");
|
|
351
|
+
/**
|
|
352
|
+
* Return the authentication method, if present.
|
|
353
|
+
* @returns <string | undefined> - the authentication method if present, otherwise undefined.
|
|
354
|
+
*/
|
|
355
|
+
const getLocalAuthMethod = () => config.get("authMethod");
|
|
356
|
+
/**
|
|
357
|
+
* Set the authentication method.
|
|
358
|
+
* @param method <string> - the authentication method to be stored.
|
|
359
|
+
*/
|
|
360
|
+
const setLocalAuthMethod = (method) => config.set("authMethod", method);
|
|
361
|
+
/**
|
|
362
|
+
* Delete the stored authentication method.
|
|
363
|
+
*/
|
|
364
|
+
const deleteLocalAuthMethod = () => config.delete("authMethod");
|
|
339
365
|
/**
|
|
340
366
|
* Get the complete local file path.
|
|
341
367
|
* @param cwd <string> - the current working directory path.
|
|
@@ -1589,20 +1615,30 @@ const checkAuth = async (firebaseApp) => {
|
|
|
1589
1615
|
const token = String(getLocalAccessToken());
|
|
1590
1616
|
let providerUserId;
|
|
1591
1617
|
let username;
|
|
1592
|
-
const
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1618
|
+
const authMethod = getLocalAuthMethod();
|
|
1619
|
+
switch (authMethod) {
|
|
1620
|
+
case "github": {
|
|
1621
|
+
// Get credentials.
|
|
1622
|
+
const credentials = exchangeGithubTokenForCredentials(token);
|
|
1623
|
+
// Sign in to Firebase using credentials.
|
|
1624
|
+
await signInToFirebase(firebaseApp, credentials);
|
|
1625
|
+
// Get Github unique identifier (handle-id).
|
|
1626
|
+
providerUserId = await getGithubProviderUserId(String(token));
|
|
1627
|
+
username = getUserHandleFromProviderUserId(providerUserId);
|
|
1628
|
+
break;
|
|
1629
|
+
}
|
|
1630
|
+
case "bandada": {
|
|
1631
|
+
const userCredentials = await signInWithCustomToken(getAuth(), token);
|
|
1632
|
+
providerUserId = userCredentials.user.uid;
|
|
1633
|
+
username = providerUserId;
|
|
1634
|
+
break;
|
|
1635
|
+
}
|
|
1636
|
+
case "siwe": {
|
|
1637
|
+
const userCredentials = await signInWithCustomToken(getAuth(), token);
|
|
1638
|
+
providerUserId = userCredentials.user.uid;
|
|
1639
|
+
username = providerUserId;
|
|
1640
|
+
break;
|
|
1641
|
+
}
|
|
1606
1642
|
}
|
|
1607
1643
|
// Get current authenticated user.
|
|
1608
1644
|
const user = getCurrentFirebaseAuthUser(firebaseApp);
|
|
@@ -2243,6 +2279,7 @@ const auth = async () => {
|
|
|
2243
2279
|
// Generate a new access token using Github Device Flow (OAuth 2.0).
|
|
2244
2280
|
const newToken = await executeGithubDeviceFlow(String(process.env.AUTH_GITHUB_CLIENT_ID));
|
|
2245
2281
|
// Store the new access token.
|
|
2282
|
+
setLocalAuthMethod("github");
|
|
2246
2283
|
setLocalAccessToken(newToken);
|
|
2247
2284
|
}
|
|
2248
2285
|
else
|
|
@@ -2285,67 +2322,220 @@ const isGroupMember = async (groupId, identity) => {
|
|
|
2285
2322
|
|
|
2286
2323
|
const { BANDADA_DASHBOARD_URL, BANDADA_GROUP_ID } = process.env;
|
|
2287
2324
|
const authBandada = async () => {
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2325
|
+
try {
|
|
2326
|
+
const { firebaseFunctions } = await bootstrapCommandExecutionAndServices();
|
|
2327
|
+
const spinner = customSpinner(`Checking identity string for Semaphore...`, `clock`);
|
|
2328
|
+
spinner.start();
|
|
2329
|
+
// 1. check if _identity string exists in local storage
|
|
2330
|
+
let identityString;
|
|
2331
|
+
const isIdentityStringStored = checkLocalBandadaIdentity();
|
|
2332
|
+
if (isIdentityStringStored) {
|
|
2333
|
+
identityString = getLocalBandadaIdentity();
|
|
2334
|
+
spinner.succeed(`Identity seed found\n`);
|
|
2335
|
+
}
|
|
2336
|
+
else {
|
|
2337
|
+
spinner.warn(`Identity seed not found\n`);
|
|
2338
|
+
// 2. generate a random _identity string and save it in local storage
|
|
2339
|
+
const { seed } = await prompts({
|
|
2340
|
+
type: "text",
|
|
2341
|
+
name: "seed",
|
|
2342
|
+
message: theme.text.bold(`Enter a secret string to use as your identity seed in Semaphore:`),
|
|
2343
|
+
initial: false
|
|
2344
|
+
});
|
|
2345
|
+
identityString = seed;
|
|
2346
|
+
setLocalBandadaIdentity(identityString);
|
|
2347
|
+
}
|
|
2348
|
+
// 3. create a semaphore identity with _identity string as a seed
|
|
2349
|
+
const identity = new Identity(identityString);
|
|
2350
|
+
// 4. check if the user is a member of the group
|
|
2351
|
+
console.log(`Checking Bandada membership...`);
|
|
2352
|
+
const isMember = await isGroupMember(BANDADA_GROUP_ID, identity);
|
|
2353
|
+
if (!isMember) {
|
|
2354
|
+
await addMemberToGroup(BANDADA_GROUP_ID, BANDADA_DASHBOARD_URL, identity);
|
|
2355
|
+
}
|
|
2356
|
+
// 5. generate a proof that the user owns the commitment.
|
|
2357
|
+
spinner.text = `Generating proof of identity...`;
|
|
2358
|
+
spinner.start();
|
|
2359
|
+
// publicSignals = [hash(externalNullifier, identityNullifier), commitment]
|
|
2360
|
+
const initDirectoryName = getLocalDirname();
|
|
2361
|
+
const directoryName = initDirectoryName.includes("/src") ? "." : initDirectoryName;
|
|
2362
|
+
const { proof, publicSignals } = await groth16.fullProve({
|
|
2363
|
+
identityTrapdoor: identity.trapdoor,
|
|
2364
|
+
identityNullifier: identity.nullifier,
|
|
2365
|
+
externalNullifier: BANDADA_GROUP_ID
|
|
2366
|
+
}, `${directoryName}/public/mini-semaphore.wasm`, `${directoryName}/public/mini-semaphore.zkey`);
|
|
2367
|
+
spinner.succeed(`Proof generated.\n`);
|
|
2368
|
+
spinner.text = `Sending proof to verification...`;
|
|
2369
|
+
spinner.start();
|
|
2370
|
+
// 6. send proof to a cloud function that verifies it and checks membership
|
|
2371
|
+
const cf = httpsCallable(firebaseFunctions, commonTerms.cloudFunctionsNames.bandadaValidateProof);
|
|
2372
|
+
const result = await cf({
|
|
2373
|
+
proof,
|
|
2374
|
+
publicSignals
|
|
2306
2375
|
});
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2376
|
+
const { valid, token, message } = result.data;
|
|
2377
|
+
if (!valid) {
|
|
2378
|
+
showError(message, true);
|
|
2379
|
+
deleteLocalAuthMethod();
|
|
2380
|
+
deleteLocalAccessToken();
|
|
2381
|
+
deleteLocalBandadaIdentity();
|
|
2382
|
+
}
|
|
2383
|
+
spinner.succeed(`Proof verified.\n`);
|
|
2384
|
+
spinner.text = `Authenticating...`;
|
|
2385
|
+
spinner.start();
|
|
2386
|
+
// 7. Auth to p0tion firebase
|
|
2387
|
+
const credentials = await signInWithCustomToken(getAuth(), token);
|
|
2388
|
+
setLocalAuthMethod("bandada");
|
|
2389
|
+
setLocalAccessToken(token);
|
|
2390
|
+
spinner.succeed(`Authenticated as ${theme.text.bold(credentials.user.uid)}.`);
|
|
2391
|
+
console.log(`\n${theme.symbols.warning} You can always log out by running the ${theme.text.bold(`phase2cli logout`)} command`);
|
|
2392
|
+
}
|
|
2393
|
+
catch (error) {
|
|
2394
|
+
// Delete local token.
|
|
2395
|
+
console.log("An error crashed the process. Deleting local token and identity.");
|
|
2396
|
+
console.error(error);
|
|
2397
|
+
deleteLocalAuthMethod();
|
|
2398
|
+
deleteLocalAccessToken();
|
|
2399
|
+
deleteLocalBandadaIdentity();
|
|
2400
|
+
}
|
|
2401
|
+
process.exit(0);
|
|
2402
|
+
};
|
|
2403
|
+
|
|
2404
|
+
const showVerificationCodeAndUri = async (OAuthDeviceCode) => {
|
|
2405
|
+
// Copy code to clipboard.
|
|
2406
|
+
let noClipboard = false;
|
|
2407
|
+
try {
|
|
2408
|
+
clipboard.writeSync(OAuthDeviceCode.user_code);
|
|
2409
|
+
clipboard.readSync();
|
|
2410
|
+
}
|
|
2411
|
+
catch (error) {
|
|
2412
|
+
noClipboard = true;
|
|
2413
|
+
}
|
|
2414
|
+
// Display data.
|
|
2415
|
+
console.log(`${theme.symbols.warning} Visit ${theme.text.bold(theme.text.underlined(OAuthDeviceCode.verification_uri))} on this device to generate a new token and authenticate\n`);
|
|
2416
|
+
console.log(theme.colors.magenta(figlet.textSync("Code is Below", { font: "ANSI Shadow" })), "\n");
|
|
2417
|
+
const message = !noClipboard ? `has been copied to your clipboard (${theme.emojis.clipboard})` : ``;
|
|
2418
|
+
console.log(`${theme.symbols.info} Your auth code: ${theme.text.bold(OAuthDeviceCode.user_code)} ${message} ${theme.symbols.success}\n`);
|
|
2419
|
+
const spinner = customSpinner(`Redirecting to Github...`, `clock`);
|
|
2329
2420
|
spinner.start();
|
|
2330
|
-
|
|
2331
|
-
|
|
2421
|
+
await sleep(10000); // ~10s to make users able to read the CLI.
|
|
2422
|
+
try {
|
|
2423
|
+
// Automatically open the page (# Step 2).
|
|
2424
|
+
await open(OAuthDeviceCode.verification_uri);
|
|
2425
|
+
}
|
|
2426
|
+
catch (error) {
|
|
2427
|
+
console.log(`${theme.symbols.info} Please authenticate via GitHub at ${OAuthDeviceCode.verification_uri}`);
|
|
2428
|
+
}
|
|
2429
|
+
spinner.stop();
|
|
2430
|
+
};
|
|
2431
|
+
/**
|
|
2432
|
+
* Return the token to sign in to Firebase after passing the SIWE Device Flow
|
|
2433
|
+
* @param clientId <string> - The client id of the Auth0 application.
|
|
2434
|
+
* @param firebaseFunctions <any> - The Firebase functions instance to call the cloud function
|
|
2435
|
+
* @returns <string> - The token to sign in to Firebase
|
|
2436
|
+
*/
|
|
2437
|
+
const executeSIWEDeviceFlow = async (clientId, firebaseFunctions) => {
|
|
2438
|
+
// Call Auth0 endpoint to request device code uri
|
|
2439
|
+
const OAuthDeviceCode = (await fetch$1(`${process.env.AUTH0_APPLICATION_URL}/oauth/device/code`, {
|
|
2440
|
+
method: "POST",
|
|
2441
|
+
headers: { "content-type": "application/json" },
|
|
2442
|
+
body: JSON.stringify({
|
|
2443
|
+
client_id: clientId,
|
|
2444
|
+
scope: "openid",
|
|
2445
|
+
audience: `${process.env.AUTH0_APPLICATION_URL}/api/v2/`
|
|
2446
|
+
})
|
|
2447
|
+
}).then((_res) => _res.json()));
|
|
2448
|
+
await showVerificationCodeAndUri(OAuthDeviceCode);
|
|
2449
|
+
// Poll Auth0 endpoint until you get token or request expires
|
|
2450
|
+
let isSignedIn = false;
|
|
2451
|
+
let isExpired = false;
|
|
2452
|
+
let auth0Token = "";
|
|
2453
|
+
while (!isSignedIn && !isExpired) {
|
|
2454
|
+
// Call Auth0 endpoint to request token
|
|
2455
|
+
const OAuthToken = (await fetch$1(`${process.env.AUTH0_APPLICATION_URL}/oauth/token`, {
|
|
2456
|
+
method: "POST",
|
|
2457
|
+
headers: { "content-type": "application/json" },
|
|
2458
|
+
body: JSON.stringify({
|
|
2459
|
+
client_id: clientId,
|
|
2460
|
+
device_code: OAuthDeviceCode.device_code,
|
|
2461
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
2462
|
+
})
|
|
2463
|
+
}).then((_res) => _res.json()));
|
|
2464
|
+
if (OAuthToken.error) {
|
|
2465
|
+
if (OAuthToken.error === "authorization_pending") {
|
|
2466
|
+
// Wait for the user to sign in
|
|
2467
|
+
await sleep(OAuthDeviceCode.interval * 1000);
|
|
2468
|
+
}
|
|
2469
|
+
else if (OAuthToken.error === "slow_down") {
|
|
2470
|
+
// Wait for the user to sign in
|
|
2471
|
+
await sleep(OAuthDeviceCode.interval * 1000 * 2);
|
|
2472
|
+
}
|
|
2473
|
+
else if (OAuthToken.error === "expired_token") {
|
|
2474
|
+
// The user didn't sign in on time
|
|
2475
|
+
isExpired = true;
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
else {
|
|
2479
|
+
// The user signed in
|
|
2480
|
+
isSignedIn = true;
|
|
2481
|
+
auth0Token = OAuthToken.access_token;
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2484
|
+
// Send token to cloud function to check nonce, create user and retrieve token
|
|
2485
|
+
const cf = httpsCallable(firebaseFunctions, commonTerms.cloudFunctionsNames.checkNonceOfSIWEAddress);
|
|
2332
2486
|
const result = await cf({
|
|
2333
|
-
|
|
2334
|
-
publicSignals
|
|
2487
|
+
auth0Token
|
|
2335
2488
|
});
|
|
2336
|
-
const {
|
|
2489
|
+
const { token, valid, message } = result.data;
|
|
2337
2490
|
if (!valid) {
|
|
2338
2491
|
showError(message, true);
|
|
2492
|
+
deleteLocalAuthMethod();
|
|
2493
|
+
deleteLocalAccessToken();
|
|
2494
|
+
}
|
|
2495
|
+
return token;
|
|
2496
|
+
};
|
|
2497
|
+
/**
|
|
2498
|
+
* Auth command using Sign In With Ethereum
|
|
2499
|
+
* @notice The auth command allows a user to make the association of their Ethereum account with the CLI by leveraging SIWE as an authentication mechanism.
|
|
2500
|
+
* @dev Under the hood, the command handles a manual Device Flow following the guidelines in the SIWE documentation.
|
|
2501
|
+
*/
|
|
2502
|
+
const authSIWE = async () => {
|
|
2503
|
+
try {
|
|
2504
|
+
const { firebaseFunctions } = await bootstrapCommandExecutionAndServices();
|
|
2505
|
+
// Console more context for the user.
|
|
2506
|
+
console.log(`${theme.symbols.info} ${theme.text.bold(`You are about to authenticate on this CLI using your Ethereum address (device flow - OAuth 2.0 mechanism).\n${theme.symbols.warning} Please, note that only a Sign-in With Ethereum signature will be required`)}\n`);
|
|
2507
|
+
const spinner = customSpinner(`Checking authentication token...`, `clock`);
|
|
2508
|
+
spinner.start();
|
|
2509
|
+
await sleep(5000);
|
|
2510
|
+
// Manage OAuth Github or SIWE token.
|
|
2511
|
+
const isLocalTokenStored = checkLocalAccessToken();
|
|
2512
|
+
if (!isLocalTokenStored) {
|
|
2513
|
+
spinner.fail(`No local authentication token found\n`);
|
|
2514
|
+
// Generate a new access token using Github Device Flow (OAuth 2.0).
|
|
2515
|
+
const newToken = await executeSIWEDeviceFlow(String(process.env.AUTH_SIWE_CLIENT_ID), firebaseFunctions);
|
|
2516
|
+
// Store the new access token.
|
|
2517
|
+
setLocalAuthMethod("siwe");
|
|
2518
|
+
setLocalAccessToken(newToken);
|
|
2519
|
+
}
|
|
2520
|
+
else
|
|
2521
|
+
spinner.succeed(`Local authentication token found\n`);
|
|
2522
|
+
// Get access token from local store.
|
|
2523
|
+
const token = String(getLocalAccessToken());
|
|
2524
|
+
spinner.text = `Authenticating...`;
|
|
2525
|
+
spinner.start();
|
|
2526
|
+
// Exchange token for credential.
|
|
2527
|
+
const credentials = await signInWithCustomToken(getAuth(), token);
|
|
2528
|
+
spinner.succeed(`Authenticated as ${theme.text.bold(credentials.user.uid)}.`);
|
|
2529
|
+
console.log(`\n${theme.symbols.warning} You can always log out by running the ${theme.text.bold(`phase2cli logout`)} command`);
|
|
2530
|
+
process.exit(0);
|
|
2531
|
+
}
|
|
2532
|
+
catch (error) {
|
|
2533
|
+
// Delete local token.
|
|
2534
|
+
console.log("An error crashed the process. Deleting local token and identity.");
|
|
2535
|
+
console.error(error);
|
|
2536
|
+
deleteLocalAuthMethod();
|
|
2537
|
+
deleteLocalAccessToken();
|
|
2339
2538
|
}
|
|
2340
|
-
spinner.succeed(`Proof verified.\n`);
|
|
2341
|
-
spinner.text = `Authenticating...`;
|
|
2342
|
-
spinner.start();
|
|
2343
|
-
// 7. Auth to p0tion firebase
|
|
2344
|
-
const userCredentials = await signInWithCustomToken(getAuth(), token);
|
|
2345
|
-
setLocalAccessToken(token);
|
|
2346
|
-
spinner.succeed(`Authenticated as ${theme.text.bold(userCredentials.user.uid)}.`);
|
|
2347
|
-
console.log(`\n${theme.symbols.warning} You can always log out by running the ${theme.text.bold(`phase2cli logout`)} command`);
|
|
2348
|
-
process.exit(0);
|
|
2349
2539
|
};
|
|
2350
2540
|
|
|
2351
2541
|
/**
|
|
@@ -2470,8 +2660,8 @@ const handleDiskSpaceRequirementForNextContribution = async (cloudFunctions, cer
|
|
|
2470
2660
|
spinner.fail(`You may not have enough memory to calculate the contribution for the Circuit ${theme.colors.magenta(`${circuitSequencePosition}`)}.\n\n${theme.symbols.info} The required amount of disk space is ${contributionDiskSpaceRequirement < 0.01
|
|
2471
2661
|
? theme.text.bold(`< 0.01`)
|
|
2472
2662
|
: theme.text.bold(contributionDiskSpaceRequirement)} GB but you only have ${participantFreeDiskSpace > 0 ? theme.text.bold(participantFreeDiskSpace.toFixed(2)) : theme.text.bold(0)} GB available memory \nThe estimate ${theme.text.bold("may not be 100% correct")} since is based on the aggregate free memory on your disks but some may not be detected!\n`);
|
|
2473
|
-
const {
|
|
2474
|
-
wannaContributeOrHaveEnoughMemory = !!
|
|
2663
|
+
const { confirmationEnoughMemory } = await askForConfirmation(`Please, we kindly ask you to continue with the contribution if you have noticed the estimate is wrong and you have enough memory in your machine`, "Continue", "Exit");
|
|
2664
|
+
wannaContributeOrHaveEnoughMemory = !!confirmationEnoughMemory;
|
|
2475
2665
|
if (circuitSequencePosition > 1) {
|
|
2476
2666
|
console.log(`${theme.symbols.info} Please note, you have time until ceremony ends to free up your memory and complete remaining contributions`);
|
|
2477
2667
|
// Asks the contributor if their wants to terminate contributions for the ceremony.
|
|
@@ -2540,8 +2730,8 @@ const handlePublicAttestation = async (firestoreDatabase, circuits, ceremonyId,
|
|
|
2540
2730
|
writeFile(getAttestationLocalFilePath(`${ceremonyPrefix}_${commonTerms.foldersAndPathsTerms.attestation}.log`), Buffer.from(publicAttestation));
|
|
2541
2731
|
await sleep(1000); // workaround for file descriptor unexpected close.
|
|
2542
2732
|
let gistUrl = "";
|
|
2543
|
-
const
|
|
2544
|
-
if (
|
|
2733
|
+
const isGithub = getLocalAuthMethod() === "github";
|
|
2734
|
+
if (isGithub) {
|
|
2545
2735
|
gistUrl = await publishGist(participantAccessToken, publicAttestation, ceremonyName, ceremonyPrefix);
|
|
2546
2736
|
console.log(`\n${theme.symbols.info} Your public attestation has been successfully posted as Github Gist (${theme.text.bold(theme.text.underlined(gistUrl))})`);
|
|
2547
2737
|
}
|
|
@@ -2597,6 +2787,7 @@ const listenToCeremonyCircuitDocumentChanges = (firestoreDatabase, ceremonyId, p
|
|
|
2597
2787
|
}
|
|
2598
2788
|
});
|
|
2599
2789
|
};
|
|
2790
|
+
let contributionInProgress = false;
|
|
2600
2791
|
/**
|
|
2601
2792
|
* Listen to current authenticated participant document changes.
|
|
2602
2793
|
* @dev this is the core business logic related to the execution of the contribute command.
|
|
@@ -2724,11 +2915,21 @@ const listenToParticipantDocumentChanges = async (firestoreDatabase, cloudFuncti
|
|
|
2724
2915
|
(!noTemporaryContributionData && resumingWithSameTemporaryData);
|
|
2725
2916
|
// Scenario (3.B).
|
|
2726
2917
|
if (isCurrentContributor && hasResumableStep && startingOrResumingContribution) {
|
|
2918
|
+
if (contributionInProgress) {
|
|
2919
|
+
console.warn(`\n${theme.symbols.warning} Received instruction to start/resume contribution but contribution is already in progress...[skipping]`);
|
|
2920
|
+
return;
|
|
2921
|
+
}
|
|
2727
2922
|
// Communicate resume / start of the contribution to participant.
|
|
2728
2923
|
await simpleLoader(`${changedContributionStep === "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */ ? `Starting` : `Resuming`} your contribution...`, `clock`, 3000);
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2924
|
+
try {
|
|
2925
|
+
contributionInProgress = true;
|
|
2926
|
+
// Start / Resume the contribution for the participant.
|
|
2927
|
+
await handleStartOrResumeContribution(cloudFunctions, firestoreDatabase, ceremony, circuit, participant, entropy, providerUserId, false, // not finalizing.
|
|
2928
|
+
circuits.length);
|
|
2929
|
+
}
|
|
2930
|
+
finally {
|
|
2931
|
+
contributionInProgress = false;
|
|
2932
|
+
}
|
|
2732
2933
|
}
|
|
2733
2934
|
// Scenario (3.A).
|
|
2734
2935
|
else if (isWaitingForContribution)
|
|
@@ -3259,6 +3460,7 @@ const logout = async () => {
|
|
|
3259
3460
|
const auth = getAuth();
|
|
3260
3461
|
await signOut(auth);
|
|
3261
3462
|
// Delete local token.
|
|
3463
|
+
deleteLocalAuthMethod();
|
|
3262
3464
|
deleteLocalAccessToken();
|
|
3263
3465
|
deleteLocalBandadaIdentity();
|
|
3264
3466
|
await sleep(3000); // ~3s.
|
|
@@ -3365,6 +3567,10 @@ program
|
|
|
3365
3567
|
.command("auth-bandada")
|
|
3366
3568
|
.description("authenticate yourself in a privacy-perserving manner using Bandada")
|
|
3367
3569
|
.action(authBandada);
|
|
3570
|
+
program
|
|
3571
|
+
.command("auth-siwe")
|
|
3572
|
+
.description("authenticate yourself using your Ethereum account (Sign In With Ethereum - SIWE)")
|
|
3573
|
+
.action(authSIWE);
|
|
3368
3574
|
program
|
|
3369
3575
|
.command("contribute")
|
|
3370
3576
|
.description("compute contributions for a Phase2 Trusted Setup ceremony circuits")
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth command using Sign In With Ethereum
|
|
3
|
+
* @notice The auth command allows a user to make the association of their Ethereum account with the CLI by leveraging SIWE as an authentication mechanism.
|
|
4
|
+
* @dev Under the hood, the command handles a manual Device Flow following the guidelines in the SIWE documentation.
|
|
5
|
+
*/
|
|
6
|
+
declare const authSIWE: () => Promise<void>;
|
|
7
|
+
export default authSIWE;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { default as setup } from "./setup.js";
|
|
2
2
|
export { default as auth } from "./auth.js";
|
|
3
3
|
export { default as authBandada } from "./authBandada.js";
|
|
4
|
+
export { default as authSIWE } from "./authSIWE.js";
|
|
4
5
|
export { default as contribute } from "./contribute.js";
|
|
5
6
|
export { default as observe } from "./observe.js";
|
|
6
7
|
export { default as finalize } from "./finalize.js";
|
|
@@ -54,6 +54,25 @@ export declare const setLocalBandadaIdentity: (identity: string) => void;
|
|
|
54
54
|
* Delete the stored Bandada identity.
|
|
55
55
|
*/
|
|
56
56
|
export declare const deleteLocalBandadaIdentity: () => void;
|
|
57
|
+
/**
|
|
58
|
+
* Return the authentication method, if present.
|
|
59
|
+
* @returns <string | undefined> - the authentication method if present, otherwise undefined.
|
|
60
|
+
*/
|
|
61
|
+
export declare const getLocalAuthMethod: () => string | unknown;
|
|
62
|
+
/**
|
|
63
|
+
* Check if the authentication method exists in the local storage.
|
|
64
|
+
* @returns <boolean>
|
|
65
|
+
*/
|
|
66
|
+
export declare const checkLocalAuthMethod: () => boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Set the authentication method.
|
|
69
|
+
* @param method <string> - the authentication method to be stored.
|
|
70
|
+
*/
|
|
71
|
+
export declare const setLocalAuthMethod: (method: string) => void;
|
|
72
|
+
/**
|
|
73
|
+
* Delete the stored authentication method.
|
|
74
|
+
*/
|
|
75
|
+
export declare const deleteLocalAuthMethod: () => void;
|
|
57
76
|
/**
|
|
58
77
|
* Get the complete local file path.
|
|
59
78
|
* @param cwd <string> - the current working directory path.
|
|
@@ -75,3 +75,54 @@ export type VerifiedBandadaResponse = {
|
|
|
75
75
|
message: string;
|
|
76
76
|
token: string;
|
|
77
77
|
};
|
|
78
|
+
/**
|
|
79
|
+
* Define the return object of the device code uri request.
|
|
80
|
+
* @typedef {Object} OAuthDeviceCodeResponse
|
|
81
|
+
* @property {string} device_code - the device code.
|
|
82
|
+
* @property {string} user_code - the user code.
|
|
83
|
+
* @property {string} verification_uri - the verification uri.
|
|
84
|
+
* @property {number} expires_in - the expiration time in seconds.
|
|
85
|
+
* @property {number} interval - the interval time in seconds.
|
|
86
|
+
* @property {string} verification_uri_complete - the complete verification uri.
|
|
87
|
+
*/
|
|
88
|
+
export type OAuthDeviceCodeResponse = {
|
|
89
|
+
device_code: string;
|
|
90
|
+
user_code: string;
|
|
91
|
+
verification_uri: string;
|
|
92
|
+
expires_in: number;
|
|
93
|
+
interval: number;
|
|
94
|
+
verification_uri_complete: string;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Define the return object of the polling endpoint
|
|
98
|
+
* @typedef {Object} OAuthTokenResponse
|
|
99
|
+
* @property {string} access_token - the resulting device flow token
|
|
100
|
+
* @property {string} token_type - token type
|
|
101
|
+
* @property {number} expires_in - when does the token expires
|
|
102
|
+
* @property {string} scope - the scope requested by the initial device flow endpoint
|
|
103
|
+
* @property {string} refresh_token - refresh token
|
|
104
|
+
* @property {string} id_token - id token
|
|
105
|
+
* @property {string} error - in case there was an error
|
|
106
|
+
* @property {string} error_description - error details
|
|
107
|
+
*/
|
|
108
|
+
export type OAuthTokenResponse = {
|
|
109
|
+
access_token: string;
|
|
110
|
+
token_type: string;
|
|
111
|
+
expires_in: number;
|
|
112
|
+
scope: string;
|
|
113
|
+
refresh_token: string;
|
|
114
|
+
id_token: string;
|
|
115
|
+
error?: string;
|
|
116
|
+
error_description?: string;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* @typedef {Object} CheckNonceOfSIWEAddressResponse
|
|
120
|
+
* @property {boolean} valid - if the checking was valid or not
|
|
121
|
+
* @property {string} message - more information about the validity
|
|
122
|
+
* @property {string} token - token to sign into Firebase
|
|
123
|
+
*/
|
|
124
|
+
export type CheckNonceOfSIWEAddressResponse = {
|
|
125
|
+
valid: boolean;
|
|
126
|
+
message: string;
|
|
127
|
+
token: string;
|
|
128
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devtion/devcli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.0-
|
|
4
|
+
"version": "0.0.0-671e653",
|
|
5
5
|
"description": "All-in-one interactive command-line for interfacing with zkSNARK Phase 2 Trusted Setup ceremonies",
|
|
6
6
|
"repository": "git@github.com:privacy-scaling-explorations/p0tion.git",
|
|
7
7
|
"homepage": "https://github.com/privacy-scaling-explorations/p0tion",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"build:watch": "rollup -c rollup.config.ts -w --configPlugin typescript",
|
|
35
35
|
"start": "ts-node --esm ./src/index.ts",
|
|
36
36
|
"auth": "yarn start auth",
|
|
37
|
+
"auth:siwe": "yarn start auth-siwe",
|
|
37
38
|
"auth:bandada": "yarn start auth-bandada",
|
|
38
39
|
"contribute": "yarn start contribute",
|
|
39
40
|
"clean": "yarn start clean",
|
|
@@ -59,6 +60,7 @@
|
|
|
59
60
|
"@types/winston": "^2.4.4",
|
|
60
61
|
"rollup-plugin-auto-external": "^2.0.0",
|
|
61
62
|
"rollup-plugin-cleanup": "^3.2.1",
|
|
63
|
+
"rollup-plugin-copy": "^3.5.0",
|
|
62
64
|
"rollup-plugin-typescript2": "^0.34.1",
|
|
63
65
|
"solc": "^0.8.19",
|
|
64
66
|
"ts-node": "^10.9.1",
|
|
@@ -102,5 +104,5 @@
|
|
|
102
104
|
"publishConfig": {
|
|
103
105
|
"access": "public"
|
|
104
106
|
},
|
|
105
|
-
"gitHead": "
|
|
107
|
+
"gitHead": "c9c799447bc2e3abd6b665fe09622ac724ea9b79"
|
|
106
108
|
}
|
package/src/commands/auth.ts
CHANGED
|
@@ -8,7 +8,12 @@ import figlet from "figlet"
|
|
|
8
8
|
import { fileURLToPath } from "url"
|
|
9
9
|
import { dirname } from "path"
|
|
10
10
|
import { GENERIC_ERRORS, showError } from "../lib/errors.js"
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
checkLocalAccessToken,
|
|
13
|
+
getLocalAccessToken,
|
|
14
|
+
setLocalAccessToken,
|
|
15
|
+
setLocalAuthMethod
|
|
16
|
+
} from "../lib/localConfigs.js"
|
|
12
17
|
import { bootstrapCommandExecutionAndServices, signInToFirebase } from "../lib/services.js"
|
|
13
18
|
import theme from "../lib/theme.js"
|
|
14
19
|
import {
|
|
@@ -171,6 +176,7 @@ const auth = async () => {
|
|
|
171
176
|
const newToken = await executeGithubDeviceFlow(String(process.env.AUTH_GITHUB_CLIENT_ID))
|
|
172
177
|
|
|
173
178
|
// Store the new access token.
|
|
179
|
+
setLocalAuthMethod("github")
|
|
174
180
|
setLocalAccessToken(newToken)
|
|
175
181
|
} else spinner.succeed(`Local authentication token found\n`)
|
|
176
182
|
|
|
@@ -3,8 +3,9 @@ import { Identity } from "@semaphore-protocol/identity"
|
|
|
3
3
|
import { commonTerms } from "@devtion/actions"
|
|
4
4
|
import { httpsCallable } from "firebase/functions"
|
|
5
5
|
import { groth16 } from "snarkjs"
|
|
6
|
-
import path from "path"
|
|
7
6
|
import { getAuth, signInWithCustomToken } from "firebase/auth"
|
|
7
|
+
import prompts from "prompts"
|
|
8
|
+
import { getLocalDirname } from "../lib/files.js"
|
|
8
9
|
import theme from "../lib/theme.js"
|
|
9
10
|
import { customSpinner } from "../lib/utils.js"
|
|
10
11
|
import { VerifiedBandadaResponse } from "../types/index.js"
|
|
@@ -13,85 +14,105 @@ import { bootstrapCommandExecutionAndServices } from "../lib/services.js"
|
|
|
13
14
|
import { addMemberToGroup, isGroupMember } from "../lib/bandada.js"
|
|
14
15
|
import {
|
|
15
16
|
checkLocalBandadaIdentity,
|
|
17
|
+
deleteLocalAccessToken,
|
|
18
|
+
deleteLocalAuthMethod,
|
|
19
|
+
deleteLocalBandadaIdentity,
|
|
16
20
|
getLocalBandadaIdentity,
|
|
17
21
|
setLocalAccessToken,
|
|
22
|
+
setLocalAuthMethod,
|
|
18
23
|
setLocalBandadaIdentity
|
|
19
24
|
} from "../lib/localConfigs.js"
|
|
20
|
-
import prompts from "prompts"
|
|
21
25
|
|
|
22
26
|
const { BANDADA_DASHBOARD_URL, BANDADA_GROUP_ID } = process.env
|
|
23
27
|
|
|
24
28
|
const authBandada = async () => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
29
|
+
try {
|
|
30
|
+
const { firebaseFunctions } = await bootstrapCommandExecutionAndServices()
|
|
31
|
+
const spinner = customSpinner(`Checking identity string for Semaphore...`, `clock`)
|
|
32
|
+
spinner.start()
|
|
33
|
+
// 1. check if _identity string exists in local storage
|
|
34
|
+
let identityString: string | unknown
|
|
35
|
+
const isIdentityStringStored = checkLocalBandadaIdentity()
|
|
36
|
+
if (isIdentityStringStored) {
|
|
37
|
+
identityString = getLocalBandadaIdentity()
|
|
38
|
+
spinner.succeed(`Identity seed found\n`)
|
|
39
|
+
} else {
|
|
40
|
+
spinner.warn(`Identity seed not found\n`)
|
|
41
|
+
// 2. generate a random _identity string and save it in local storage
|
|
42
|
+
const { seed } = await prompts({
|
|
43
|
+
type: "text",
|
|
44
|
+
name: "seed",
|
|
45
|
+
message: theme.text.bold(`Enter a secret string to use as your identity seed in Semaphore:`),
|
|
46
|
+
initial: false
|
|
47
|
+
})
|
|
48
|
+
identityString = seed as string
|
|
49
|
+
setLocalBandadaIdentity(identityString as string)
|
|
50
|
+
}
|
|
51
|
+
// 3. create a semaphore identity with _identity string as a seed
|
|
52
|
+
const identity = new Identity(identityString as string)
|
|
48
53
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
// 4. check if the user is a member of the group
|
|
55
|
+
console.log(`Checking Bandada membership...`)
|
|
56
|
+
const isMember = await isGroupMember(BANDADA_GROUP_ID, identity)
|
|
57
|
+
if (!isMember) {
|
|
58
|
+
await addMemberToGroup(BANDADA_GROUP_ID, BANDADA_DASHBOARD_URL, identity)
|
|
59
|
+
}
|
|
55
60
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
identityNullifier: identity.nullifier,
|
|
64
|
-
externalNullifier: BANDADA_GROUP_ID
|
|
65
|
-
},
|
|
66
|
-
path.join(path.resolve(), "/public/mini-semaphore.wasm"),
|
|
67
|
-
path.join(path.resolve(), "/public/mini-semaphore.zkey")
|
|
68
|
-
)
|
|
69
|
-
spinner.succeed(`Proof generated.\n`)
|
|
70
|
-
spinner.text = `Sending proof to verification...`
|
|
71
|
-
spinner.start()
|
|
72
|
-
// 6. send proof to a cloud function that verifies it and checks membership
|
|
73
|
-
const cf = httpsCallable(firebaseFunctions, commonTerms.cloudFunctionsNames.bandadaValidateProof)
|
|
74
|
-
const result = await cf({
|
|
75
|
-
proof,
|
|
76
|
-
publicSignals
|
|
77
|
-
})
|
|
78
|
-
const { valid, token, message } = result.data as VerifiedBandadaResponse
|
|
79
|
-
if (!valid) {
|
|
80
|
-
showError(message, true)
|
|
81
|
-
}
|
|
82
|
-
spinner.succeed(`Proof verified.\n`)
|
|
83
|
-
spinner.text = `Authenticating...`
|
|
84
|
-
spinner.start()
|
|
85
|
-
// 7. Auth to p0tion firebase
|
|
86
|
-
const userCredentials = await signInWithCustomToken(getAuth(), token)
|
|
87
|
-
setLocalAccessToken(token)
|
|
88
|
-
spinner.succeed(`Authenticated as ${theme.text.bold(userCredentials.user.uid)}.`)
|
|
61
|
+
// 5. generate a proof that the user owns the commitment.
|
|
62
|
+
spinner.text = `Generating proof of identity...`
|
|
63
|
+
spinner.start()
|
|
64
|
+
// publicSignals = [hash(externalNullifier, identityNullifier), commitment]
|
|
65
|
+
|
|
66
|
+
const initDirectoryName = getLocalDirname()
|
|
67
|
+
const directoryName = initDirectoryName.includes("/src") ? "." : initDirectoryName
|
|
89
68
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
69
|
+
const { proof, publicSignals } = await groth16.fullProve(
|
|
70
|
+
{
|
|
71
|
+
identityTrapdoor: identity.trapdoor,
|
|
72
|
+
identityNullifier: identity.nullifier,
|
|
73
|
+
externalNullifier: BANDADA_GROUP_ID
|
|
74
|
+
},
|
|
75
|
+
`${directoryName}/public/mini-semaphore.wasm`,
|
|
76
|
+
`${directoryName}/public/mini-semaphore.zkey`
|
|
77
|
+
)
|
|
78
|
+
spinner.succeed(`Proof generated.\n`)
|
|
79
|
+
spinner.text = `Sending proof to verification...`
|
|
80
|
+
spinner.start()
|
|
81
|
+
// 6. send proof to a cloud function that verifies it and checks membership
|
|
82
|
+
const cf = httpsCallable(firebaseFunctions, commonTerms.cloudFunctionsNames.bandadaValidateProof)
|
|
83
|
+
const result = await cf({
|
|
84
|
+
proof,
|
|
85
|
+
publicSignals
|
|
86
|
+
})
|
|
87
|
+
const { valid, token, message } = result.data as VerifiedBandadaResponse
|
|
88
|
+
if (!valid) {
|
|
89
|
+
showError(message, true)
|
|
90
|
+
deleteLocalAuthMethod()
|
|
91
|
+
deleteLocalAccessToken()
|
|
92
|
+
deleteLocalBandadaIdentity()
|
|
93
|
+
}
|
|
94
|
+
spinner.succeed(`Proof verified.\n`)
|
|
95
|
+
spinner.text = `Authenticating...`
|
|
96
|
+
spinner.start()
|
|
97
|
+
// 7. Auth to p0tion firebase
|
|
98
|
+
const credentials = await signInWithCustomToken(getAuth(), token)
|
|
99
|
+
setLocalAuthMethod("bandada")
|
|
100
|
+
setLocalAccessToken(token)
|
|
101
|
+
spinner.succeed(`Authenticated as ${theme.text.bold(credentials.user.uid)}.`)
|
|
102
|
+
|
|
103
|
+
console.log(
|
|
104
|
+
`\n${theme.symbols.warning} You can always log out by running the ${theme.text.bold(
|
|
105
|
+
`phase2cli logout`
|
|
106
|
+
)} command`
|
|
107
|
+
)
|
|
108
|
+
} catch (error) {
|
|
109
|
+
// Delete local token.
|
|
110
|
+
console.log("An error crashed the process. Deleting local token and identity.")
|
|
111
|
+
console.error(error)
|
|
112
|
+
deleteLocalAuthMethod()
|
|
113
|
+
deleteLocalAccessToken()
|
|
114
|
+
deleteLocalBandadaIdentity()
|
|
115
|
+
}
|
|
95
116
|
|
|
96
117
|
process.exit(0)
|
|
97
118
|
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import open from "open"
|
|
2
|
+
import figlet from "figlet"
|
|
3
|
+
import clipboard from "clipboardy"
|
|
4
|
+
import fetch from "node-fetch"
|
|
5
|
+
import { getAuth, signInWithCustomToken } from "firebase/auth"
|
|
6
|
+
import { httpsCallable } from "firebase/functions"
|
|
7
|
+
import { commonTerms } from "@devtion/actions"
|
|
8
|
+
import { showError } from "../lib/errors.js"
|
|
9
|
+
import { bootstrapCommandExecutionAndServices } from "../lib/services.js"
|
|
10
|
+
import theme from "../lib/theme.js"
|
|
11
|
+
import { customSpinner, sleep } from "../lib/utils.js"
|
|
12
|
+
import { CheckNonceOfSIWEAddressResponse, OAuthDeviceCodeResponse, OAuthTokenResponse } from "../types/index.js"
|
|
13
|
+
import {
|
|
14
|
+
checkLocalAccessToken,
|
|
15
|
+
deleteLocalAccessToken,
|
|
16
|
+
deleteLocalAuthMethod,
|
|
17
|
+
getLocalAccessToken,
|
|
18
|
+
setLocalAccessToken,
|
|
19
|
+
setLocalAuthMethod
|
|
20
|
+
} from "../lib/localConfigs.js"
|
|
21
|
+
|
|
22
|
+
const showVerificationCodeAndUri = async (OAuthDeviceCode: OAuthDeviceCodeResponse) => {
|
|
23
|
+
// Copy code to clipboard.
|
|
24
|
+
let noClipboard = false
|
|
25
|
+
try {
|
|
26
|
+
clipboard.writeSync(OAuthDeviceCode.user_code)
|
|
27
|
+
clipboard.readSync()
|
|
28
|
+
} catch (error) {
|
|
29
|
+
noClipboard = true
|
|
30
|
+
}
|
|
31
|
+
// Display data.
|
|
32
|
+
console.log(
|
|
33
|
+
`${theme.symbols.warning} Visit ${theme.text.bold(
|
|
34
|
+
theme.text.underlined(OAuthDeviceCode.verification_uri)
|
|
35
|
+
)} on this device to generate a new token and authenticate\n`
|
|
36
|
+
)
|
|
37
|
+
console.log(theme.colors.magenta(figlet.textSync("Code is Below", { font: "ANSI Shadow" })), "\n")
|
|
38
|
+
|
|
39
|
+
const message = !noClipboard ? `has been copied to your clipboard (${theme.emojis.clipboard})` : ``
|
|
40
|
+
console.log(
|
|
41
|
+
`${theme.symbols.info} Your auth code: ${theme.text.bold(OAuthDeviceCode.user_code)} ${message} ${
|
|
42
|
+
theme.symbols.success
|
|
43
|
+
}\n`
|
|
44
|
+
)
|
|
45
|
+
const spinner = customSpinner(`Redirecting to Github...`, `clock`)
|
|
46
|
+
spinner.start()
|
|
47
|
+
await sleep(10000) // ~10s to make users able to read the CLI.
|
|
48
|
+
try {
|
|
49
|
+
// Automatically open the page (# Step 2).
|
|
50
|
+
await open(OAuthDeviceCode.verification_uri)
|
|
51
|
+
} catch (error: any) {
|
|
52
|
+
console.log(`${theme.symbols.info} Please authenticate via GitHub at ${OAuthDeviceCode.verification_uri}`)
|
|
53
|
+
}
|
|
54
|
+
spinner.stop()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Return the token to sign in to Firebase after passing the SIWE Device Flow
|
|
59
|
+
* @param clientId <string> - The client id of the Auth0 application.
|
|
60
|
+
* @param firebaseFunctions <any> - The Firebase functions instance to call the cloud function
|
|
61
|
+
* @returns <string> - The token to sign in to Firebase
|
|
62
|
+
*/
|
|
63
|
+
const executeSIWEDeviceFlow = async (clientId: string, firebaseFunctions: any): Promise<string> => {
|
|
64
|
+
// Call Auth0 endpoint to request device code uri
|
|
65
|
+
const OAuthDeviceCode = (await fetch(`${process.env.AUTH0_APPLICATION_URL}/oauth/device/code`, {
|
|
66
|
+
method: "POST",
|
|
67
|
+
headers: { "content-type": "application/json" },
|
|
68
|
+
body: JSON.stringify({
|
|
69
|
+
client_id: clientId,
|
|
70
|
+
scope: "openid",
|
|
71
|
+
audience: `${process.env.AUTH0_APPLICATION_URL}/api/v2/`
|
|
72
|
+
})
|
|
73
|
+
}).then((_res) => _res.json())) as OAuthDeviceCodeResponse
|
|
74
|
+
await showVerificationCodeAndUri(OAuthDeviceCode)
|
|
75
|
+
// Poll Auth0 endpoint until you get token or request expires
|
|
76
|
+
let isSignedIn = false
|
|
77
|
+
let isExpired = false
|
|
78
|
+
let auth0Token = ""
|
|
79
|
+
while (!isSignedIn && !isExpired) {
|
|
80
|
+
// Call Auth0 endpoint to request token
|
|
81
|
+
const OAuthToken = (await fetch(`${process.env.AUTH0_APPLICATION_URL}/oauth/token`, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: { "content-type": "application/json" },
|
|
84
|
+
body: JSON.stringify({
|
|
85
|
+
client_id: clientId,
|
|
86
|
+
device_code: OAuthDeviceCode.device_code,
|
|
87
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
88
|
+
})
|
|
89
|
+
}).then((_res) => _res.json())) as OAuthTokenResponse
|
|
90
|
+
if (OAuthToken.error) {
|
|
91
|
+
if (OAuthToken.error === "authorization_pending") {
|
|
92
|
+
// Wait for the user to sign in
|
|
93
|
+
await sleep(OAuthDeviceCode.interval * 1000)
|
|
94
|
+
} else if (OAuthToken.error === "slow_down") {
|
|
95
|
+
// Wait for the user to sign in
|
|
96
|
+
await sleep(OAuthDeviceCode.interval * 1000 * 2)
|
|
97
|
+
} else if (OAuthToken.error === "expired_token") {
|
|
98
|
+
// The user didn't sign in on time
|
|
99
|
+
isExpired = true
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
// The user signed in
|
|
103
|
+
isSignedIn = true
|
|
104
|
+
auth0Token = OAuthToken.access_token
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Send token to cloud function to check nonce, create user and retrieve token
|
|
108
|
+
const cf = httpsCallable(firebaseFunctions, commonTerms.cloudFunctionsNames.checkNonceOfSIWEAddress)
|
|
109
|
+
const result = await cf({
|
|
110
|
+
auth0Token
|
|
111
|
+
})
|
|
112
|
+
const { token, valid, message } = result.data as CheckNonceOfSIWEAddressResponse
|
|
113
|
+
if (!valid) {
|
|
114
|
+
showError(message, true)
|
|
115
|
+
deleteLocalAuthMethod()
|
|
116
|
+
deleteLocalAccessToken()
|
|
117
|
+
}
|
|
118
|
+
return token
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Auth command using Sign In With Ethereum
|
|
123
|
+
* @notice The auth command allows a user to make the association of their Ethereum account with the CLI by leveraging SIWE as an authentication mechanism.
|
|
124
|
+
* @dev Under the hood, the command handles a manual Device Flow following the guidelines in the SIWE documentation.
|
|
125
|
+
*/
|
|
126
|
+
const authSIWE = async () => {
|
|
127
|
+
try {
|
|
128
|
+
const { firebaseFunctions } = await bootstrapCommandExecutionAndServices()
|
|
129
|
+
// Console more context for the user.
|
|
130
|
+
console.log(
|
|
131
|
+
`${theme.symbols.info} ${theme.text.bold(
|
|
132
|
+
`You are about to authenticate on this CLI using your Ethereum address (device flow - OAuth 2.0 mechanism).\n${theme.symbols.warning} Please, note that only a Sign-in With Ethereum signature will be required`
|
|
133
|
+
)}\n`
|
|
134
|
+
)
|
|
135
|
+
const spinner = customSpinner(`Checking authentication token...`, `clock`)
|
|
136
|
+
spinner.start()
|
|
137
|
+
await sleep(5000)
|
|
138
|
+
|
|
139
|
+
// Manage OAuth Github or SIWE token.
|
|
140
|
+
const isLocalTokenStored = checkLocalAccessToken()
|
|
141
|
+
|
|
142
|
+
if (!isLocalTokenStored) {
|
|
143
|
+
spinner.fail(`No local authentication token found\n`)
|
|
144
|
+
|
|
145
|
+
// Generate a new access token using Github Device Flow (OAuth 2.0).
|
|
146
|
+
const newToken = await executeSIWEDeviceFlow(String(process.env.AUTH_SIWE_CLIENT_ID), firebaseFunctions)
|
|
147
|
+
|
|
148
|
+
// Store the new access token.
|
|
149
|
+
setLocalAuthMethod("siwe")
|
|
150
|
+
setLocalAccessToken(newToken)
|
|
151
|
+
} else spinner.succeed(`Local authentication token found\n`)
|
|
152
|
+
|
|
153
|
+
// Get access token from local store.
|
|
154
|
+
const token = String(getLocalAccessToken())
|
|
155
|
+
|
|
156
|
+
spinner.text = `Authenticating...`
|
|
157
|
+
spinner.start()
|
|
158
|
+
|
|
159
|
+
// Exchange token for credential.
|
|
160
|
+
const credentials = await signInWithCustomToken(getAuth(), token)
|
|
161
|
+
spinner.succeed(`Authenticated as ${theme.text.bold(credentials.user.uid)}.`)
|
|
162
|
+
|
|
163
|
+
console.log(
|
|
164
|
+
`\n${theme.symbols.warning} You can always log out by running the ${theme.text.bold(
|
|
165
|
+
`phase2cli logout`
|
|
166
|
+
)} command`
|
|
167
|
+
)
|
|
168
|
+
process.exit(0)
|
|
169
|
+
} catch (error) {
|
|
170
|
+
// Delete local token.
|
|
171
|
+
console.log("An error crashed the process. Deleting local token and identity.")
|
|
172
|
+
console.error(error)
|
|
173
|
+
deleteLocalAuthMethod()
|
|
174
|
+
deleteLocalAccessToken()
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export default authSIWE
|
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
} from "../lib/utils.js"
|
|
42
42
|
import { COMMAND_ERRORS, showError } from "../lib/errors.js"
|
|
43
43
|
import { authWithToken, bootstrapCommandExecutionAndServices, checkAuth } from "../lib/services.js"
|
|
44
|
-
import {
|
|
44
|
+
import { getAttestationLocalFilePath, getLocalAuthMethod, localPaths } from "../lib/localConfigs.js"
|
|
45
45
|
import theme from "../lib/theme.js"
|
|
46
46
|
import { checkAndMakeNewDirectoryIfNonexistent, writeFile } from "../lib/files.js"
|
|
47
47
|
|
|
@@ -281,12 +281,12 @@ export const handleDiskSpaceRequirementForNextContribution = async (
|
|
|
281
281
|
)} since is based on the aggregate free memory on your disks but some may not be detected!\n`
|
|
282
282
|
)
|
|
283
283
|
|
|
284
|
-
const {
|
|
284
|
+
const { confirmationEnoughMemory } = await askForConfirmation(
|
|
285
285
|
`Please, we kindly ask you to continue with the contribution if you have noticed the estimate is wrong and you have enough memory in your machine`,
|
|
286
286
|
"Continue",
|
|
287
287
|
"Exit"
|
|
288
288
|
)
|
|
289
|
-
wannaContributeOrHaveEnoughMemory = !!
|
|
289
|
+
wannaContributeOrHaveEnoughMemory = !!confirmationEnoughMemory
|
|
290
290
|
|
|
291
291
|
if (circuitSequencePosition > 1) {
|
|
292
292
|
console.log(
|
|
@@ -420,8 +420,8 @@ export const handlePublicAttestation = async (
|
|
|
420
420
|
await sleep(1000) // workaround for file descriptor unexpected close.
|
|
421
421
|
|
|
422
422
|
let gistUrl = ""
|
|
423
|
-
const
|
|
424
|
-
if (
|
|
423
|
+
const isGithub = getLocalAuthMethod() === "github"
|
|
424
|
+
if (isGithub) {
|
|
425
425
|
gistUrl = await publishGist(participantAccessToken, publicAttestation, ceremonyName, ceremonyPrefix)
|
|
426
426
|
|
|
427
427
|
console.log(
|
|
@@ -519,6 +519,8 @@ export const listenToCeremonyCircuitDocumentChanges = (
|
|
|
519
519
|
})
|
|
520
520
|
}
|
|
521
521
|
|
|
522
|
+
let contributionInProgress = false
|
|
523
|
+
|
|
522
524
|
/**
|
|
523
525
|
* Listen to current authenticated participant document changes.
|
|
524
526
|
* @dev this is the core business logic related to the execution of the contribute command.
|
|
@@ -711,6 +713,12 @@ export const listenToParticipantDocumentChanges = async (
|
|
|
711
713
|
|
|
712
714
|
// Scenario (3.B).
|
|
713
715
|
if (isCurrentContributor && hasResumableStep && startingOrResumingContribution) {
|
|
716
|
+
if (contributionInProgress) {
|
|
717
|
+
console.warn(
|
|
718
|
+
`\n${theme.symbols.warning} Received instruction to start/resume contribution but contribution is already in progress...[skipping]`
|
|
719
|
+
)
|
|
720
|
+
return
|
|
721
|
+
}
|
|
714
722
|
// Communicate resume / start of the contribution to participant.
|
|
715
723
|
await simpleLoader(
|
|
716
724
|
`${
|
|
@@ -720,18 +728,24 @@ export const listenToParticipantDocumentChanges = async (
|
|
|
720
728
|
3000
|
|
721
729
|
)
|
|
722
730
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
731
|
+
try {
|
|
732
|
+
contributionInProgress = true
|
|
733
|
+
|
|
734
|
+
// Start / Resume the contribution for the participant.
|
|
735
|
+
await handleStartOrResumeContribution(
|
|
736
|
+
cloudFunctions,
|
|
737
|
+
firestoreDatabase,
|
|
738
|
+
ceremony,
|
|
739
|
+
circuit,
|
|
740
|
+
participant,
|
|
741
|
+
entropy,
|
|
742
|
+
providerUserId,
|
|
743
|
+
false, // not finalizing.
|
|
744
|
+
circuits.length
|
|
745
|
+
)
|
|
746
|
+
} finally {
|
|
747
|
+
contributionInProgress = false
|
|
748
|
+
}
|
|
735
749
|
}
|
|
736
750
|
// Scenario (3.A).
|
|
737
751
|
else if (isWaitingForContribution)
|
package/src/commands/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { default as setup } from "./setup.js"
|
|
2
2
|
export { default as auth } from "./auth.js"
|
|
3
3
|
export { default as authBandada } from "./authBandada.js"
|
|
4
|
+
export { default as authSIWE } from "./authSIWE.js"
|
|
4
5
|
export { default as contribute } from "./contribute.js"
|
|
5
6
|
export { default as observe } from "./observe.js"
|
|
6
7
|
export { default as finalize } from "./finalize.js"
|
package/src/commands/logout.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { showError } from "../lib/errors.js"
|
|
|
6
6
|
import { askForConfirmation } from "../lib/prompts.js"
|
|
7
7
|
import { customSpinner, sleep, terminate } from "../lib/utils.js"
|
|
8
8
|
import theme from "../lib/theme.js"
|
|
9
|
-
import { deleteLocalAccessToken, deleteLocalBandadaIdentity } from "../lib/localConfigs.js"
|
|
9
|
+
import { deleteLocalAccessToken, deleteLocalAuthMethod, deleteLocalBandadaIdentity } from "../lib/localConfigs.js"
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Logout command.
|
|
@@ -52,6 +52,7 @@ const logout = async () => {
|
|
|
52
52
|
await signOut(auth)
|
|
53
53
|
|
|
54
54
|
// Delete local token.
|
|
55
|
+
deleteLocalAuthMethod()
|
|
55
56
|
deleteLocalAccessToken()
|
|
56
57
|
deleteLocalBandadaIdentity()
|
|
57
58
|
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { fileURLToPath } from "url"
|
|
|
7
7
|
import {
|
|
8
8
|
setup,
|
|
9
9
|
auth,
|
|
10
|
+
authSIWE,
|
|
10
11
|
authBandada,
|
|
11
12
|
contribute,
|
|
12
13
|
observe,
|
|
@@ -32,6 +33,10 @@ program
|
|
|
32
33
|
.command("auth-bandada")
|
|
33
34
|
.description("authenticate yourself in a privacy-perserving manner using Bandada")
|
|
34
35
|
.action(authBandada)
|
|
36
|
+
program
|
|
37
|
+
.command("auth-siwe")
|
|
38
|
+
.description("authenticate yourself using your Ethereum account (Sign In With Ethereum - SIWE)")
|
|
39
|
+
.action(authSIWE)
|
|
35
40
|
program
|
|
36
41
|
.command("contribute")
|
|
37
42
|
.description("compute contributions for a Phase2 Trusted Setup ceremony circuits")
|
package/src/lib/localConfigs.ts
CHANGED
|
@@ -28,6 +28,10 @@ const config = new Conf({
|
|
|
28
28
|
bandadaIdentity: {
|
|
29
29
|
type: "string",
|
|
30
30
|
default: ""
|
|
31
|
+
},
|
|
32
|
+
authMethod: {
|
|
33
|
+
type: "string",
|
|
34
|
+
default: ""
|
|
31
35
|
}
|
|
32
36
|
}
|
|
33
37
|
})
|
|
@@ -118,6 +122,29 @@ export const setLocalBandadaIdentity = (identity: string) => config.set("bandada
|
|
|
118
122
|
*/
|
|
119
123
|
export const deleteLocalBandadaIdentity = () => config.delete("bandadaIdentity")
|
|
120
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Return the authentication method, if present.
|
|
127
|
+
* @returns <string | undefined> - the authentication method if present, otherwise undefined.
|
|
128
|
+
*/
|
|
129
|
+
export const getLocalAuthMethod = (): string | unknown => config.get("authMethod")
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Check if the authentication method exists in the local storage.
|
|
133
|
+
* @returns <boolean>
|
|
134
|
+
*/
|
|
135
|
+
export const checkLocalAuthMethod = (): boolean => config.has("authMethod") && !!config.get("authMethod")
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Set the authentication method.
|
|
139
|
+
* @param method <string> - the authentication method to be stored.
|
|
140
|
+
*/
|
|
141
|
+
export const setLocalAuthMethod = (method: string) => config.set("authMethod", method)
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Delete the stored authentication method.
|
|
145
|
+
*/
|
|
146
|
+
export const deleteLocalAuthMethod = () => config.delete("authMethod")
|
|
147
|
+
|
|
121
148
|
/**
|
|
122
149
|
* Get the complete local file path.
|
|
123
150
|
* @param cwd <string> - the current working directory path.
|
package/src/lib/services.ts
CHANGED
|
@@ -14,9 +14,9 @@ import { AuthUser } from "../types/index.js"
|
|
|
14
14
|
import { CONFIG_ERRORS, CORE_SERVICES_ERRORS, showError, THIRD_PARTY_SERVICES_ERRORS } from "./errors.js"
|
|
15
15
|
import {
|
|
16
16
|
checkLocalAccessToken,
|
|
17
|
-
checkLocalBandadaIdentity,
|
|
18
17
|
deleteLocalAccessToken,
|
|
19
|
-
getLocalAccessToken
|
|
18
|
+
getLocalAccessToken,
|
|
19
|
+
getLocalAuthMethod
|
|
20
20
|
} from "./localConfigs.js"
|
|
21
21
|
import theme from "./theme.js"
|
|
22
22
|
import { exchangeGithubTokenForCredentials, getGithubProviderUserId, getUserHandleFromProviderUserId } from "./utils.js"
|
|
@@ -171,21 +171,33 @@ export const checkAuth = async (firebaseApp: FirebaseApp): Promise<AuthUser> =>
|
|
|
171
171
|
|
|
172
172
|
let providerUserId: string
|
|
173
173
|
let username: string
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
174
|
+
const authMethod = getLocalAuthMethod()
|
|
175
|
+
switch (authMethod) {
|
|
176
|
+
case "github": {
|
|
177
|
+
// Get credentials.
|
|
178
|
+
const credentials = exchangeGithubTokenForCredentials(token)
|
|
179
|
+
// Sign in to Firebase using credentials.
|
|
180
|
+
await signInToFirebase(firebaseApp, credentials)
|
|
181
|
+
// Get Github unique identifier (handle-id).
|
|
182
|
+
providerUserId = await getGithubProviderUserId(String(token))
|
|
183
|
+
username = getUserHandleFromProviderUserId(providerUserId)
|
|
184
|
+
break
|
|
185
|
+
}
|
|
186
|
+
case "bandada": {
|
|
187
|
+
const userCredentials = await signInWithCustomToken(getAuth(), token)
|
|
188
|
+
providerUserId = userCredentials.user.uid
|
|
189
|
+
username = providerUserId
|
|
190
|
+
break
|
|
191
|
+
}
|
|
192
|
+
case "siwe": {
|
|
193
|
+
const userCredentials = await signInWithCustomToken(getAuth(), token)
|
|
194
|
+
providerUserId = userCredentials.user.uid
|
|
195
|
+
username = providerUserId
|
|
196
|
+
break
|
|
197
|
+
}
|
|
198
|
+
default: {
|
|
199
|
+
break
|
|
200
|
+
}
|
|
189
201
|
}
|
|
190
202
|
|
|
191
203
|
// Get current authenticated user.
|
package/src/types/index.ts
CHANGED
|
@@ -81,3 +81,58 @@ export type VerifiedBandadaResponse = {
|
|
|
81
81
|
message: string
|
|
82
82
|
token: string
|
|
83
83
|
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Define the return object of the device code uri request.
|
|
87
|
+
* @typedef {Object} OAuthDeviceCodeResponse
|
|
88
|
+
* @property {string} device_code - the device code.
|
|
89
|
+
* @property {string} user_code - the user code.
|
|
90
|
+
* @property {string} verification_uri - the verification uri.
|
|
91
|
+
* @property {number} expires_in - the expiration time in seconds.
|
|
92
|
+
* @property {number} interval - the interval time in seconds.
|
|
93
|
+
* @property {string} verification_uri_complete - the complete verification uri.
|
|
94
|
+
*/
|
|
95
|
+
export type OAuthDeviceCodeResponse = {
|
|
96
|
+
device_code: string
|
|
97
|
+
user_code: string
|
|
98
|
+
verification_uri: string
|
|
99
|
+
expires_in: number
|
|
100
|
+
interval: number
|
|
101
|
+
verification_uri_complete: string
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Define the return object of the polling endpoint
|
|
106
|
+
* @typedef {Object} OAuthTokenResponse
|
|
107
|
+
* @property {string} access_token - the resulting device flow token
|
|
108
|
+
* @property {string} token_type - token type
|
|
109
|
+
* @property {number} expires_in - when does the token expires
|
|
110
|
+
* @property {string} scope - the scope requested by the initial device flow endpoint
|
|
111
|
+
* @property {string} refresh_token - refresh token
|
|
112
|
+
* @property {string} id_token - id token
|
|
113
|
+
* @property {string} error - in case there was an error
|
|
114
|
+
* @property {string} error_description - error details
|
|
115
|
+
*/
|
|
116
|
+
export type OAuthTokenResponse = {
|
|
117
|
+
access_token: string
|
|
118
|
+
token_type: string
|
|
119
|
+
expires_in: number
|
|
120
|
+
scope: string
|
|
121
|
+
refresh_token: string
|
|
122
|
+
id_token: string
|
|
123
|
+
// error response should contain
|
|
124
|
+
error?: string
|
|
125
|
+
error_description?: string
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @typedef {Object} CheckNonceOfSIWEAddressResponse
|
|
130
|
+
* @property {boolean} valid - if the checking was valid or not
|
|
131
|
+
* @property {string} message - more information about the validity
|
|
132
|
+
* @property {string} token - token to sign into Firebase
|
|
133
|
+
*/
|
|
134
|
+
export type CheckNonceOfSIWEAddressResponse = {
|
|
135
|
+
valid: boolean
|
|
136
|
+
message: string
|
|
137
|
+
token: string
|
|
138
|
+
}
|