@devtion/backend 0.0.0-b499eaf → 0.0.0-c1f4cbe

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.
@@ -27,10 +27,13 @@ var path = require('path');
27
27
  var os = require('os');
28
28
  var clientSsm = require('@aws-sdk/client-ssm');
29
29
  var clientEc2 = require('@aws-sdk/client-ec2');
30
+ var ethers = require('ethers');
30
31
  var functionsV1 = require('firebase-functions/v1');
31
32
  var functionsV2 = require('firebase-functions/v2');
32
33
  var timerNode = require('timer-node');
33
34
  var snarkjs = require('snarkjs');
35
+ var apiSdk = require('@bandada/api-sdk');
36
+ var auth = require('firebase-admin/auth');
34
37
 
35
38
  function _interopNamespaceDefault(e) {
36
39
  var n = Object.create(null);
@@ -72,7 +75,7 @@ var LogLevel;
72
75
  * @notice the set of Firebase Functions status codes. The codes are the same at the
73
76
  * ones exposed by {@link https://github.com/grpc/grpc/blob/master/doc/statuscodes.md | gRPC}.
74
77
  * @param errorCode <FunctionsErrorCode> - the set of possible error codes.
75
- * @param message <string> - the error messge.
78
+ * @param message <string> - the error message.
76
79
  * @param [details] <string> - the details of the error (optional).
77
80
  * @returns <HttpsError>
78
81
  */
@@ -164,6 +167,8 @@ const COMMON_ERRORS = {
164
167
  CM_INVALID_COMMAND_EXECUTION: makeError("unknown", "There was an error while executing the command on the VM", "Please, contact the coordinator if the error persists.")
165
168
  };
166
169
 
170
+ dotenv.config();
171
+ let provider;
167
172
  /**
168
173
  * Return a configured and connected instance of the AWS S3 client.
169
174
  * @dev this method check and utilize the environment variables to configure the connection
@@ -186,6 +191,36 @@ const getS3Client = async () => {
186
191
  region: process.env.AWS_REGION
187
192
  });
188
193
  };
194
+ /**
195
+ * Returns a Prvider, connected via a configured JSON URL or else
196
+ * the ethers.js default provider, using configured API keys.
197
+ * @returns <ethers.providers.Provider> An Eth node provider
198
+ */
199
+ const setEthProvider = () => {
200
+ if (provider)
201
+ return provider;
202
+ console.log(`setting new provider`);
203
+ // Use JSON URL if defined
204
+ // if ((hardhat as any).ethers) {
205
+ // console.log(`using hardhat.ethers provider`)
206
+ // provider = (hardhat as any).ethers.provider
207
+ // } else
208
+ if (process.env.ETH_PROVIDER_JSON_URL) {
209
+ console.log(`JSON URL provider at ${process.env.ETH_PROVIDER_JSON_URL}`);
210
+ provider = new ethers.providers.JsonRpcProvider({
211
+ url: process.env.ETH_PROVIDER_JSON_URL,
212
+ skipFetchSetup: true
213
+ });
214
+ }
215
+ else {
216
+ // Otherwise, connect the default provider with ALchemy, Infura, or both
217
+ provider = ethers.providers.getDefaultProvider("homestead", {
218
+ alchemy: process.env.ETH_PROVIDER_ALCHEMY_API_KEY,
219
+ infura: process.env.ETH_PROVIDER_INFURA_API_KEY
220
+ });
221
+ }
222
+ return provider;
223
+ };
189
224
 
190
225
  dotenv.config();
191
226
  /**
@@ -556,7 +591,7 @@ const registerAuthUser = functions__namespace
556
591
  email === process.env.CUSTOM_CLAIMS_COORDINATOR_EMAIL_ADDRESS_OR_DOMAIN)) {
557
592
  const auth = admin.auth();
558
593
  // if provider == github.com let's use our functions to check the user's reputation
559
- if (user.providerData[0].providerId === "github.com") {
594
+ if (user.providerData.length > 0 && user.providerData[0].providerId === "github.com") {
560
595
  const vars = getGitHubVariables();
561
596
  // this return true or false
562
597
  try {
@@ -588,7 +623,7 @@ const registerAuthUser = functions__namespace
588
623
  encodedDisplayName,
589
624
  // Metadata.
590
625
  creationTime,
591
- lastSignInTime,
626
+ lastSignInTime: lastSignInTime || creationTime,
592
627
  // Optional.
593
628
  email: email || "",
594
629
  emailVerified: emailVerified || false,
@@ -2407,6 +2442,216 @@ const completeMultiPartUpload = functions__namespace
2407
2442
  }
2408
2443
  });
2409
2444
 
2445
+ const VKEY_DATA = {
2446
+ protocol: "groth16",
2447
+ curve: "bn128",
2448
+ nPublic: 3,
2449
+ vk_alpha_1: [
2450
+ "20491192805390485299153009773594534940189261866228447918068658471970481763042",
2451
+ "9383485363053290200918347156157836566562967994039712273449902621266178545958",
2452
+ "1"
2453
+ ],
2454
+ vk_beta_2: [
2455
+ [
2456
+ "6375614351688725206403948262868962793625744043794305715222011528459656738731",
2457
+ "4252822878758300859123897981450591353533073413197771768651442665752259397132"
2458
+ ],
2459
+ [
2460
+ "10505242626370262277552901082094356697409835680220590971873171140371331206856",
2461
+ "21847035105528745403288232691147584728191162732299865338377159692350059136679"
2462
+ ],
2463
+ ["1", "0"]
2464
+ ],
2465
+ vk_gamma_2: [
2466
+ [
2467
+ "10857046999023057135944570762232829481370756359578518086990519993285655852781",
2468
+ "11559732032986387107991004021392285783925812861821192530917403151452391805634"
2469
+ ],
2470
+ [
2471
+ "8495653923123431417604973247489272438418190587263600148770280649306958101930",
2472
+ "4082367875863433681332203403145435568316851327593401208105741076214120093531"
2473
+ ],
2474
+ ["1", "0"]
2475
+ ],
2476
+ vk_delta_2: [
2477
+ [
2478
+ "3697618915467790705869942236922063775466274665053173890632463796679068973252",
2479
+ "14948341351907992175709156460547989243732741534604949238422596319735704165658"
2480
+ ],
2481
+ [
2482
+ "3028459181652799888716942141752307629938889957960373621898607910203491239368",
2483
+ "11380736494786911280692284374675752681598754560757720296073023058533044108340"
2484
+ ],
2485
+ ["1", "0"]
2486
+ ],
2487
+ vk_alphabeta_12: [
2488
+ [
2489
+ [
2490
+ "2029413683389138792403550203267699914886160938906632433982220835551125967885",
2491
+ "21072700047562757817161031222997517981543347628379360635925549008442030252106"
2492
+ ],
2493
+ [
2494
+ "5940354580057074848093997050200682056184807770593307860589430076672439820312",
2495
+ "12156638873931618554171829126792193045421052652279363021382169897324752428276"
2496
+ ],
2497
+ [
2498
+ "7898200236362823042373859371574133993780991612861777490112507062703164551277",
2499
+ "7074218545237549455313236346927434013100842096812539264420499035217050630853"
2500
+ ]
2501
+ ],
2502
+ [
2503
+ [
2504
+ "7077479683546002997211712695946002074877511277312570035766170199895071832130",
2505
+ "10093483419865920389913245021038182291233451549023025229112148274109565435465"
2506
+ ],
2507
+ [
2508
+ "4595479056700221319381530156280926371456704509942304414423590385166031118820",
2509
+ "19831328484489333784475432780421641293929726139240675179672856274388269393268"
2510
+ ],
2511
+ [
2512
+ "11934129596455521040620786944827826205713621633706285934057045369193958244500",
2513
+ "8037395052364110730298837004334506829870972346962140206007064471173334027475"
2514
+ ]
2515
+ ]
2516
+ ],
2517
+ IC: [
2518
+ [
2519
+ "12951059800758687233303204819298121944551181861362200875212570257618182506154",
2520
+ "5751958719396509176593242305268064754837298673622815112953832050159760501392",
2521
+ "1"
2522
+ ],
2523
+ [
2524
+ "9561588427935871983444704959674198910445823619407211599507208879011862515257",
2525
+ "14576201570478094842467636169770180675293504492823217349086195663150934064643",
2526
+ "1"
2527
+ ],
2528
+ [
2529
+ "4811967233483727873912563574622036989372099129165459921963463310078093941559",
2530
+ "1874883809855039536107616044787862082553628089593740724610117059083415551067",
2531
+ "1"
2532
+ ],
2533
+ [
2534
+ "12252730267779308452229639835051322390696643456253768618882001876621526827161",
2535
+ "7899194018737016222260328309937800777948677569409898603827268776967707173231",
2536
+ "1"
2537
+ ]
2538
+ ]
2539
+ };
2540
+ dotenv.config();
2541
+ const { BANDADA_API_URL, BANDADA_GROUP_ID } = process.env;
2542
+ const bandadaApi = new apiSdk.ApiSdk(BANDADA_API_URL);
2543
+ const bandadaValidateProof = functions__namespace
2544
+ .region("europe-west1")
2545
+ .runWith({
2546
+ memory: "512MB"
2547
+ })
2548
+ .https.onCall(async (data) => {
2549
+ if (!BANDADA_GROUP_ID)
2550
+ throw new Error("BANDADA_GROUP_ID is not defined in .env");
2551
+ const { proof, publicSignals } = data;
2552
+ const isCorrect = snarkjs.groth16.verify(VKEY_DATA, publicSignals, proof);
2553
+ if (!isCorrect)
2554
+ return {
2555
+ valid: false,
2556
+ message: "Invalid proof",
2557
+ token: ""
2558
+ };
2559
+ const commitment = data.publicSignals[1];
2560
+ const isMember = await bandadaApi.isGroupMember(BANDADA_GROUP_ID, commitment);
2561
+ if (!isMember)
2562
+ return {
2563
+ valid: false,
2564
+ message: "Not a member of the group",
2565
+ token: ""
2566
+ };
2567
+ const auth$1 = auth.getAuth();
2568
+ try {
2569
+ await admin.auth().createUser({
2570
+ uid: commitment
2571
+ });
2572
+ }
2573
+ catch (error) {
2574
+ // if user already exist then just pass
2575
+ if (error.code !== "auth/uid-already-exists") {
2576
+ throw new Error(error);
2577
+ }
2578
+ }
2579
+ const token = await auth$1.createCustomToken(commitment);
2580
+ return {
2581
+ valid: true,
2582
+ message: "Valid proof and group member",
2583
+ token
2584
+ };
2585
+ });
2586
+
2587
+ dotenv.config();
2588
+ const checkNonceOfSIWEAddress = functions__namespace
2589
+ .region("europe-west1")
2590
+ .runWith({ memory: "1GB" })
2591
+ .https.onCall(async (data) => {
2592
+ try {
2593
+ const { auth0Token } = data;
2594
+ const result = (await fetch(`${process.env.AUTH0_APPLICATION_URL}/userinfo`, {
2595
+ method: "GET",
2596
+ headers: {
2597
+ "content-type": "application/json",
2598
+ authorization: `Bearer ${auth0Token}`
2599
+ }
2600
+ }).then((_res) => _res.json()));
2601
+ if (!result.sub) {
2602
+ return {
2603
+ valid: false,
2604
+ message: "No user detected. Please check device flow token"
2605
+ };
2606
+ }
2607
+ const auth$1 = auth.getAuth();
2608
+ // check nonce
2609
+ const parts = result.sub.split("|");
2610
+ const address = decodeURIComponent(parts[2]).split("eip155:534352:")[1];
2611
+ const minimumNonce = Number(process.env.ETH_MINIMUM_NONCE);
2612
+ const nonceBlockHeight = "latest"; // process.env.ETH_NONCE_BLOCK_HEIGHT
2613
+ // look up nonce for address @block
2614
+ let nonceOk = true;
2615
+ if (minimumNonce > 0) {
2616
+ const provider = setEthProvider();
2617
+ console.log(`got provider - block # ${await provider.getBlockNumber()}`);
2618
+ const nonce = await provider.getTransactionCount(address, nonceBlockHeight);
2619
+ console.log(`nonce ${nonce}`);
2620
+ nonceOk = nonce >= minimumNonce;
2621
+ }
2622
+ console.log(`checking nonce ${nonceOk}`);
2623
+ if (!nonceOk) {
2624
+ return {
2625
+ valid: false,
2626
+ message: "Eth address does not meet the nonce requirements"
2627
+ };
2628
+ }
2629
+ try {
2630
+ await admin.auth().createUser({
2631
+ displayName: address,
2632
+ uid: address
2633
+ });
2634
+ }
2635
+ catch (error) {
2636
+ // if user already exist then just pass
2637
+ if (error.code !== "auth/uid-already-exists") {
2638
+ throw new Error(error);
2639
+ }
2640
+ }
2641
+ const token = await auth$1.createCustomToken(address);
2642
+ return {
2643
+ valid: true,
2644
+ token
2645
+ };
2646
+ }
2647
+ catch (error) {
2648
+ return {
2649
+ valid: false,
2650
+ message: `Something went wrong ${error}`
2651
+ };
2652
+ }
2653
+ });
2654
+
2410
2655
  dotenv.config();
2411
2656
  /**
2412
2657
  * Check and remove the current contributor if it doesn't complete the contribution on the specified amount of time.
@@ -2608,9 +2853,11 @@ const resumeContributionAfterTimeoutExpiration = functions__namespace
2608
2853
 
2609
2854
  admin.initializeApp();
2610
2855
 
2856
+ exports.bandadaValidateProof = bandadaValidateProof;
2611
2857
  exports.checkAndPrepareCoordinatorForFinalization = checkAndPrepareCoordinatorForFinalization;
2612
2858
  exports.checkAndRemoveBlockingContributor = checkAndRemoveBlockingContributor;
2613
2859
  exports.checkIfObjectExist = checkIfObjectExist;
2860
+ exports.checkNonceOfSIWEAddress = checkNonceOfSIWEAddress;
2614
2861
  exports.checkParticipantForCeremony = checkParticipantForCeremony;
2615
2862
  exports.completeMultiPartUpload = completeMultiPartUpload;
2616
2863
  exports.coordinateCeremonyParticipant = coordinateCeremonyParticipant;
@@ -25,10 +25,13 @@ import path from 'path';
25
25
  import os from 'os';
26
26
  import { SSMClient, CommandInvocationStatus } from '@aws-sdk/client-ssm';
27
27
  import { EC2Client } from '@aws-sdk/client-ec2';
28
+ import ethers from 'ethers';
28
29
  import * as functionsV1 from 'firebase-functions/v1';
29
30
  import * as functionsV2 from 'firebase-functions/v2';
30
31
  import { Timer } from 'timer-node';
31
- import { zKey } from 'snarkjs';
32
+ import { zKey, groth16 } from 'snarkjs';
33
+ import { ApiSdk } from '@bandada/api-sdk';
34
+ import { getAuth } from 'firebase-admin/auth';
32
35
 
33
36
  /**
34
37
  * Log levels.
@@ -49,7 +52,7 @@ var LogLevel;
49
52
  * @notice the set of Firebase Functions status codes. The codes are the same at the
50
53
  * ones exposed by {@link https://github.com/grpc/grpc/blob/master/doc/statuscodes.md | gRPC}.
51
54
  * @param errorCode <FunctionsErrorCode> - the set of possible error codes.
52
- * @param message <string> - the error messge.
55
+ * @param message <string> - the error message.
53
56
  * @param [details] <string> - the details of the error (optional).
54
57
  * @returns <HttpsError>
55
58
  */
@@ -141,6 +144,8 @@ const COMMON_ERRORS = {
141
144
  CM_INVALID_COMMAND_EXECUTION: makeError("unknown", "There was an error while executing the command on the VM", "Please, contact the coordinator if the error persists.")
142
145
  };
143
146
 
147
+ dotenv.config();
148
+ let provider;
144
149
  /**
145
150
  * Return a configured and connected instance of the AWS S3 client.
146
151
  * @dev this method check and utilize the environment variables to configure the connection
@@ -163,6 +168,36 @@ const getS3Client = async () => {
163
168
  region: process.env.AWS_REGION
164
169
  });
165
170
  };
171
+ /**
172
+ * Returns a Prvider, connected via a configured JSON URL or else
173
+ * the ethers.js default provider, using configured API keys.
174
+ * @returns <ethers.providers.Provider> An Eth node provider
175
+ */
176
+ const setEthProvider = () => {
177
+ if (provider)
178
+ return provider;
179
+ console.log(`setting new provider`);
180
+ // Use JSON URL if defined
181
+ // if ((hardhat as any).ethers) {
182
+ // console.log(`using hardhat.ethers provider`)
183
+ // provider = (hardhat as any).ethers.provider
184
+ // } else
185
+ if (process.env.ETH_PROVIDER_JSON_URL) {
186
+ console.log(`JSON URL provider at ${process.env.ETH_PROVIDER_JSON_URL}`);
187
+ provider = new ethers.providers.JsonRpcProvider({
188
+ url: process.env.ETH_PROVIDER_JSON_URL,
189
+ skipFetchSetup: true
190
+ });
191
+ }
192
+ else {
193
+ // Otherwise, connect the default provider with ALchemy, Infura, or both
194
+ provider = ethers.providers.getDefaultProvider("homestead", {
195
+ alchemy: process.env.ETH_PROVIDER_ALCHEMY_API_KEY,
196
+ infura: process.env.ETH_PROVIDER_INFURA_API_KEY
197
+ });
198
+ }
199
+ return provider;
200
+ };
166
201
 
167
202
  dotenv.config();
168
203
  /**
@@ -533,7 +568,7 @@ const registerAuthUser = functions
533
568
  email === process.env.CUSTOM_CLAIMS_COORDINATOR_EMAIL_ADDRESS_OR_DOMAIN)) {
534
569
  const auth = admin.auth();
535
570
  // if provider == github.com let's use our functions to check the user's reputation
536
- if (user.providerData[0].providerId === "github.com") {
571
+ if (user.providerData.length > 0 && user.providerData[0].providerId === "github.com") {
537
572
  const vars = getGitHubVariables();
538
573
  // this return true or false
539
574
  try {
@@ -565,7 +600,7 @@ const registerAuthUser = functions
565
600
  encodedDisplayName,
566
601
  // Metadata.
567
602
  creationTime,
568
- lastSignInTime,
603
+ lastSignInTime: lastSignInTime || creationTime,
569
604
  // Optional.
570
605
  email: email || "",
571
606
  emailVerified: emailVerified || false,
@@ -2384,6 +2419,216 @@ const completeMultiPartUpload = functions
2384
2419
  }
2385
2420
  });
2386
2421
 
2422
+ const VKEY_DATA = {
2423
+ protocol: "groth16",
2424
+ curve: "bn128",
2425
+ nPublic: 3,
2426
+ vk_alpha_1: [
2427
+ "20491192805390485299153009773594534940189261866228447918068658471970481763042",
2428
+ "9383485363053290200918347156157836566562967994039712273449902621266178545958",
2429
+ "1"
2430
+ ],
2431
+ vk_beta_2: [
2432
+ [
2433
+ "6375614351688725206403948262868962793625744043794305715222011528459656738731",
2434
+ "4252822878758300859123897981450591353533073413197771768651442665752259397132"
2435
+ ],
2436
+ [
2437
+ "10505242626370262277552901082094356697409835680220590971873171140371331206856",
2438
+ "21847035105528745403288232691147584728191162732299865338377159692350059136679"
2439
+ ],
2440
+ ["1", "0"]
2441
+ ],
2442
+ vk_gamma_2: [
2443
+ [
2444
+ "10857046999023057135944570762232829481370756359578518086990519993285655852781",
2445
+ "11559732032986387107991004021392285783925812861821192530917403151452391805634"
2446
+ ],
2447
+ [
2448
+ "8495653923123431417604973247489272438418190587263600148770280649306958101930",
2449
+ "4082367875863433681332203403145435568316851327593401208105741076214120093531"
2450
+ ],
2451
+ ["1", "0"]
2452
+ ],
2453
+ vk_delta_2: [
2454
+ [
2455
+ "3697618915467790705869942236922063775466274665053173890632463796679068973252",
2456
+ "14948341351907992175709156460547989243732741534604949238422596319735704165658"
2457
+ ],
2458
+ [
2459
+ "3028459181652799888716942141752307629938889957960373621898607910203491239368",
2460
+ "11380736494786911280692284374675752681598754560757720296073023058533044108340"
2461
+ ],
2462
+ ["1", "0"]
2463
+ ],
2464
+ vk_alphabeta_12: [
2465
+ [
2466
+ [
2467
+ "2029413683389138792403550203267699914886160938906632433982220835551125967885",
2468
+ "21072700047562757817161031222997517981543347628379360635925549008442030252106"
2469
+ ],
2470
+ [
2471
+ "5940354580057074848093997050200682056184807770593307860589430076672439820312",
2472
+ "12156638873931618554171829126792193045421052652279363021382169897324752428276"
2473
+ ],
2474
+ [
2475
+ "7898200236362823042373859371574133993780991612861777490112507062703164551277",
2476
+ "7074218545237549455313236346927434013100842096812539264420499035217050630853"
2477
+ ]
2478
+ ],
2479
+ [
2480
+ [
2481
+ "7077479683546002997211712695946002074877511277312570035766170199895071832130",
2482
+ "10093483419865920389913245021038182291233451549023025229112148274109565435465"
2483
+ ],
2484
+ [
2485
+ "4595479056700221319381530156280926371456704509942304414423590385166031118820",
2486
+ "19831328484489333784475432780421641293929726139240675179672856274388269393268"
2487
+ ],
2488
+ [
2489
+ "11934129596455521040620786944827826205713621633706285934057045369193958244500",
2490
+ "8037395052364110730298837004334506829870972346962140206007064471173334027475"
2491
+ ]
2492
+ ]
2493
+ ],
2494
+ IC: [
2495
+ [
2496
+ "12951059800758687233303204819298121944551181861362200875212570257618182506154",
2497
+ "5751958719396509176593242305268064754837298673622815112953832050159760501392",
2498
+ "1"
2499
+ ],
2500
+ [
2501
+ "9561588427935871983444704959674198910445823619407211599507208879011862515257",
2502
+ "14576201570478094842467636169770180675293504492823217349086195663150934064643",
2503
+ "1"
2504
+ ],
2505
+ [
2506
+ "4811967233483727873912563574622036989372099129165459921963463310078093941559",
2507
+ "1874883809855039536107616044787862082553628089593740724610117059083415551067",
2508
+ "1"
2509
+ ],
2510
+ [
2511
+ "12252730267779308452229639835051322390696643456253768618882001876621526827161",
2512
+ "7899194018737016222260328309937800777948677569409898603827268776967707173231",
2513
+ "1"
2514
+ ]
2515
+ ]
2516
+ };
2517
+ dotenv.config();
2518
+ const { BANDADA_API_URL, BANDADA_GROUP_ID } = process.env;
2519
+ const bandadaApi = new ApiSdk(BANDADA_API_URL);
2520
+ const bandadaValidateProof = functions
2521
+ .region("europe-west1")
2522
+ .runWith({
2523
+ memory: "512MB"
2524
+ })
2525
+ .https.onCall(async (data) => {
2526
+ if (!BANDADA_GROUP_ID)
2527
+ throw new Error("BANDADA_GROUP_ID is not defined in .env");
2528
+ const { proof, publicSignals } = data;
2529
+ const isCorrect = groth16.verify(VKEY_DATA, publicSignals, proof);
2530
+ if (!isCorrect)
2531
+ return {
2532
+ valid: false,
2533
+ message: "Invalid proof",
2534
+ token: ""
2535
+ };
2536
+ const commitment = data.publicSignals[1];
2537
+ const isMember = await bandadaApi.isGroupMember(BANDADA_GROUP_ID, commitment);
2538
+ if (!isMember)
2539
+ return {
2540
+ valid: false,
2541
+ message: "Not a member of the group",
2542
+ token: ""
2543
+ };
2544
+ const auth = getAuth();
2545
+ try {
2546
+ await admin.auth().createUser({
2547
+ uid: commitment
2548
+ });
2549
+ }
2550
+ catch (error) {
2551
+ // if user already exist then just pass
2552
+ if (error.code !== "auth/uid-already-exists") {
2553
+ throw new Error(error);
2554
+ }
2555
+ }
2556
+ const token = await auth.createCustomToken(commitment);
2557
+ return {
2558
+ valid: true,
2559
+ message: "Valid proof and group member",
2560
+ token
2561
+ };
2562
+ });
2563
+
2564
+ dotenv.config();
2565
+ const checkNonceOfSIWEAddress = functions
2566
+ .region("europe-west1")
2567
+ .runWith({ memory: "1GB" })
2568
+ .https.onCall(async (data) => {
2569
+ try {
2570
+ const { auth0Token } = data;
2571
+ const result = (await fetch(`${process.env.AUTH0_APPLICATION_URL}/userinfo`, {
2572
+ method: "GET",
2573
+ headers: {
2574
+ "content-type": "application/json",
2575
+ authorization: `Bearer ${auth0Token}`
2576
+ }
2577
+ }).then((_res) => _res.json()));
2578
+ if (!result.sub) {
2579
+ return {
2580
+ valid: false,
2581
+ message: "No user detected. Please check device flow token"
2582
+ };
2583
+ }
2584
+ const auth = getAuth();
2585
+ // check nonce
2586
+ const parts = result.sub.split("|");
2587
+ const address = decodeURIComponent(parts[2]).split("eip155:534352:")[1];
2588
+ const minimumNonce = Number(process.env.ETH_MINIMUM_NONCE);
2589
+ const nonceBlockHeight = "latest"; // process.env.ETH_NONCE_BLOCK_HEIGHT
2590
+ // look up nonce for address @block
2591
+ let nonceOk = true;
2592
+ if (minimumNonce > 0) {
2593
+ const provider = setEthProvider();
2594
+ console.log(`got provider - block # ${await provider.getBlockNumber()}`);
2595
+ const nonce = await provider.getTransactionCount(address, nonceBlockHeight);
2596
+ console.log(`nonce ${nonce}`);
2597
+ nonceOk = nonce >= minimumNonce;
2598
+ }
2599
+ console.log(`checking nonce ${nonceOk}`);
2600
+ if (!nonceOk) {
2601
+ return {
2602
+ valid: false,
2603
+ message: "Eth address does not meet the nonce requirements"
2604
+ };
2605
+ }
2606
+ try {
2607
+ await admin.auth().createUser({
2608
+ displayName: address,
2609
+ uid: address
2610
+ });
2611
+ }
2612
+ catch (error) {
2613
+ // if user already exist then just pass
2614
+ if (error.code !== "auth/uid-already-exists") {
2615
+ throw new Error(error);
2616
+ }
2617
+ }
2618
+ const token = await auth.createCustomToken(address);
2619
+ return {
2620
+ valid: true,
2621
+ token
2622
+ };
2623
+ }
2624
+ catch (error) {
2625
+ return {
2626
+ valid: false,
2627
+ message: `Something went wrong ${error}`
2628
+ };
2629
+ }
2630
+ });
2631
+
2387
2632
  dotenv.config();
2388
2633
  /**
2389
2634
  * Check and remove the current contributor if it doesn't complete the contribution on the specified amount of time.
@@ -2585,4 +2830,4 @@ const resumeContributionAfterTimeoutExpiration = functions
2585
2830
 
2586
2831
  admin.initializeApp();
2587
2832
 
2588
- export { checkAndPrepareCoordinatorForFinalization, checkAndRemoveBlockingContributor, checkIfObjectExist, checkParticipantForCeremony, completeMultiPartUpload, coordinateCeremonyParticipant, createBucket, finalizeCeremony, finalizeCircuit, generateGetObjectPreSignedUrl, generatePreSignedUrlsParts, initEmptyWaitingQueueForCircuit, permanentlyStoreCurrentContributionTimeAndHash, processSignUpWithCustomClaims, progressToNextCircuitForContribution, progressToNextContributionStep, refreshParticipantAfterContributionVerification, registerAuthUser, resumeContributionAfterTimeoutExpiration, setupCeremony, startCeremony, startMultiPartUpload, stopCeremony, temporaryStoreCurrentContributionMultiPartUploadId, temporaryStoreCurrentContributionUploadedChunkData, verifycontribution };
2833
+ export { bandadaValidateProof, checkAndPrepareCoordinatorForFinalization, checkAndRemoveBlockingContributor, checkIfObjectExist, checkNonceOfSIWEAddress, checkParticipantForCeremony, completeMultiPartUpload, coordinateCeremonyParticipant, createBucket, finalizeCeremony, finalizeCircuit, generateGetObjectPreSignedUrl, generatePreSignedUrlsParts, initEmptyWaitingQueueForCircuit, permanentlyStoreCurrentContributionTimeAndHash, processSignUpWithCustomClaims, progressToNextCircuitForContribution, progressToNextContributionStep, refreshParticipantAfterContributionVerification, registerAuthUser, resumeContributionAfterTimeoutExpiration, setupCeremony, startCeremony, startMultiPartUpload, stopCeremony, temporaryStoreCurrentContributionMultiPartUploadId, temporaryStoreCurrentContributionUploadedChunkData, verifycontribution };
@@ -0,0 +1,4 @@
1
+ import * as functions from "firebase-functions";
2
+ export declare const bandadaValidateProof: functions.HttpsFunction & functions.Runnable<any>;
3
+ export default bandadaValidateProof;
4
+ //# sourceMappingURL=bandada.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bandada.d.ts","sourceRoot":"","sources":["../../../src/functions/bandada.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAA;AA6G/C,eAAO,MAAM,oBAAoB,mDA0C3B,CAAA;AAEN,eAAe,oBAAoB,CAAA"}
@@ -3,5 +3,7 @@ export { startCeremony, stopCeremony, setupCeremony, initEmptyWaitingQueueForCir
3
3
  export { checkParticipantForCeremony, progressToNextContributionStep, permanentlyStoreCurrentContributionTimeAndHash, temporaryStoreCurrentContributionMultiPartUploadId, temporaryStoreCurrentContributionUploadedChunkData, progressToNextCircuitForContribution, checkAndPrepareCoordinatorForFinalization } from "./participant";
4
4
  export { coordinateCeremonyParticipant, verifycontribution, refreshParticipantAfterContributionVerification, finalizeCircuit } from "./circuit";
5
5
  export { createBucket, checkIfObjectExist, generateGetObjectPreSignedUrl, startMultiPartUpload, generatePreSignedUrlsParts, completeMultiPartUpload } from "./storage";
6
+ export { bandadaValidateProof } from "./bandada";
7
+ export { checkNonceOfSIWEAddress } from "./siwe";
6
8
  export { checkAndRemoveBlockingContributor, resumeContributionAfterTimeoutExpiration } from "./timeout";
7
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/functions/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,6BAA6B,EAAE,MAAM,QAAQ,CAAA;AACxE,OAAO,EACH,aAAa,EACb,YAAY,EACZ,aAAa,EACb,+BAA+B,EAC/B,gBAAgB,EACnB,MAAM,YAAY,CAAA;AACnB,OAAO,EACH,2BAA2B,EAC3B,8BAA8B,EAC9B,8CAA8C,EAC9C,kDAAkD,EAClD,kDAAkD,EAClD,oCAAoC,EACpC,yCAAyC,EAC5C,MAAM,eAAe,CAAA;AACtB,OAAO,EACH,6BAA6B,EAC7B,kBAAkB,EAClB,+CAA+C,EAC/C,eAAe,EAClB,MAAM,WAAW,CAAA;AAClB,OAAO,EACH,YAAY,EACZ,kBAAkB,EAClB,6BAA6B,EAC7B,oBAAoB,EACpB,0BAA0B,EAC1B,uBAAuB,EAC1B,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,iCAAiC,EAAE,wCAAwC,EAAE,MAAM,WAAW,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/functions/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,6BAA6B,EAAE,MAAM,QAAQ,CAAA;AACxE,OAAO,EACH,aAAa,EACb,YAAY,EACZ,aAAa,EACb,+BAA+B,EAC/B,gBAAgB,EACnB,MAAM,YAAY,CAAA;AACnB,OAAO,EACH,2BAA2B,EAC3B,8BAA8B,EAC9B,8CAA8C,EAC9C,kDAAkD,EAClD,kDAAkD,EAClD,oCAAoC,EACpC,yCAAyC,EAC5C,MAAM,eAAe,CAAA;AACtB,OAAO,EACH,6BAA6B,EAC7B,kBAAkB,EAClB,+CAA+C,EAC/C,eAAe,EAClB,MAAM,WAAW,CAAA;AAClB,OAAO,EACH,YAAY,EACZ,kBAAkB,EAClB,6BAA6B,EAC7B,oBAAoB,EACpB,0BAA0B,EAC1B,uBAAuB,EAC1B,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,EAAE,uBAAuB,EAAE,MAAM,QAAQ,CAAA;AAChD,OAAO,EAAE,iCAAiC,EAAE,wCAAwC,EAAE,MAAM,WAAW,CAAA"}
@@ -0,0 +1,4 @@
1
+ import * as functions from "firebase-functions";
2
+ export declare const checkNonceOfSIWEAddress: functions.HttpsFunction & functions.Runnable<any>;
3
+ export default checkNonceOfSIWEAddress;
4
+ //# sourceMappingURL=siwe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"siwe.d.ts","sourceRoot":"","sources":["../../../src/functions/siwe.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAA;AAQ/C,eAAO,MAAM,uBAAuB,mDAgE9B,CAAA;AAEN,eAAe,uBAAuB,CAAA"}
@@ -6,7 +6,7 @@ import { LogLevel } from "../types/enums";
6
6
  * @notice the set of Firebase Functions status codes. The codes are the same at the
7
7
  * ones exposed by {@link https://github.com/grpc/grpc/blob/master/doc/statuscodes.md | gRPC}.
8
8
  * @param errorCode <FunctionsErrorCode> - the set of possible error codes.
9
- * @param message <string> - the error messge.
9
+ * @param message <string> - the error message.
10
10
  * @param [details] <string> - the details of the error (optional).
11
11
  * @returns <HttpsError>
12
12
  */
@@ -1,3 +1,4 @@
1
+ import ethers from "ethers";
1
2
  import { S3Client } from "@aws-sdk/client-s3";
2
3
  /**
3
4
  * Return a configured and connected instance of the AWS S3 client.
@@ -6,4 +7,10 @@ import { S3Client } from "@aws-sdk/client-s3";
6
7
  * @returns <Promise<S3Client>> - the instance of the connected S3 Client instance.
7
8
  */
8
9
  export declare const getS3Client: () => Promise<S3Client>;
10
+ /**
11
+ * Returns a Prvider, connected via a configured JSON URL or else
12
+ * the ethers.js default provider, using configured API keys.
13
+ * @returns <ethers.providers.Provider> An Eth node provider
14
+ */
15
+ export declare const setEthProvider: () => ethers.providers.Provider;
9
16
  //# sourceMappingURL=services.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../../src/lib/services.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAG7C;;;;;GAKG;AACH,eAAO,MAAM,WAAW,QAAa,QAAQ,QAAQ,CAkBpD,CAAA"}
1
+ {"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../../src/lib/services.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,QAAQ,CAAA;AAC3B,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAM7C;;;;;GAKG;AACH,eAAO,MAAM,WAAW,QAAa,QAAQ,QAAQ,CAkBpD,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAAO,OAAO,SAAS,CAAC,QAwBlD,CAAA"}
@@ -1,4 +1,5 @@
1
1
  import { CeremonyInputData, CircuitDocument, ETagWithPartNumber } from "@devtion/actions";
2
+ import type { Groth16Proof, PublicSignals } from "snarkjs";
2
3
  /**
3
4
  * Group all the necessary data needed for running the `setupCeremony` cloud function.
4
5
  * @typedef {Object} SetupCeremonyData
@@ -127,4 +128,59 @@ export type FinalizeCircuitData = {
127
128
  bucketName: string;
128
129
  beacon: string;
129
130
  };
131
+ /**
132
+ * Group all the necessary data needed for running the `bandadaValidateProof` cloud function.
133
+ * @typedef {Object} BandadaValidateProof
134
+ * @property {string} merkleTreeRoot - the merkle tree root of the group.
135
+ * @property {string} nullifierHash - the nullifier hash of the member.
136
+ * @property {string} externalNullifier - the external nullifier of the member.
137
+ * @property {PackedProof} proof - the packed proof generated on the client.
138
+ */
139
+ export type BandadaValidateProof = {
140
+ proof: Groth16Proof;
141
+ publicSignals: PublicSignals;
142
+ };
143
+ /**
144
+ * Define the return object of the function that verifies the Bandada membership and proof.
145
+ * @typedef {Object} VerifiedBandadaResponse
146
+ * @property {boolean} valid - true if the proof is valid and the user is a member of the group; otherwise false.
147
+ * @property {string} message - a message describing the result of the verification.
148
+ * @property {string} token - the custom access token.
149
+ */
150
+ export type VerifiedBandadaResponse = {
151
+ valid: boolean;
152
+ message: string;
153
+ token: string;
154
+ };
155
+ /**
156
+ * Define the check nonce object for the cloud function
157
+ * @typedef {Object} CheckNonceOfSIWEAddressRequest
158
+ * @property {string} auth0Token - token from the device flow authentication
159
+ */
160
+ export type CheckNonceOfSIWEAddressRequest = {
161
+ auth0Token: string;
162
+ };
163
+ /**
164
+ * Define the check nonce response object of the cloud function
165
+ * @typedef {Object} CheckNonceOfSIWEAddressResponse
166
+ * @property {boolean} valid - if the checking result was valid or not
167
+ * @property {string} message - informative message
168
+ * @property {string} token - token to sign in
169
+ */
170
+ export type CheckNonceOfSIWEAddressResponse = {
171
+ valid: boolean;
172
+ message?: string;
173
+ token?: string;
174
+ };
175
+ /**
176
+ * Define the response from auth0 /userinfo endpoint
177
+ *
178
+ */
179
+ export type Auth0UserInfo = {
180
+ sub: string;
181
+ nickname: string;
182
+ name: string;
183
+ picture: string;
184
+ updated_at: string;
185
+ };
130
186
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAExF;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC5B,iBAAiB,EAAE,iBAAiB,CAAA;IACpC,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,EAAE,KAAK,CAAC,eAAe,CAAC,CAAA;CACnC,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC3B,UAAU,EAAE,MAAM,CAAA;CACrB,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;CACpB,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,wBAAwB,GAAG,sBAAsB,GAAG;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,8BAA8B,GAAG,sBAAsB,GAAG;IAClE,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,2BAA2B,GAAG,sBAAsB,GAAG;IAC/D,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,KAAK,CAAC,kBAAkB,CAAC,CAAA;IAChC,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,8CAA8C,GAAG;IACzD,UAAU,EAAE,MAAM,CAAA;IAClB,2BAA2B,EAAE,MAAM,CAAA;IACnC,gBAAgB,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,kDAAkD,GAAG;IAC7D,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,kDAAkD,GAAG;IAC7D,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,kBAAkB,CAAA;CAC5B,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,kCAAkC,EAAE,MAAM,CAAA;CAC7C,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAC9B,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;CACjB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devtion/backend",
3
- "version": "0.0.0-b499eaf",
3
+ "version": "0.0.0-c1f4cbe",
4
4
  "description": "MPC Phase 2 backend for Firebase services management",
5
5
  "repository": "git@github.com:privacy-scaling-explorations/p0tion.git",
6
6
  "homepage": "https://github.com/privacy-scaling-explorations/p0tion",
@@ -67,6 +67,7 @@
67
67
  "@aws-sdk/client-ssm": "^3.357.0",
68
68
  "@aws-sdk/middleware-endpoint": "^3.329.0",
69
69
  "@aws-sdk/s3-request-presigner": "^3.329.0",
70
+ "@bandada/api-sdk": "^1.0.0-beta.1",
70
71
  "@devtion/actions": "latest",
71
72
  "blakejs": "^1.2.1",
72
73
  "dotenv": "^16.0.3",
@@ -76,7 +77,7 @@
76
77
  "html-entities": "^2.3.3",
77
78
  "rimraf": "^5.0.0",
78
79
  "rollup": "^3.21.6",
79
- "snarkjs": "^0.6.11",
80
+ "snarkjs": "0.7.3",
80
81
  "solc": "^0.8.19",
81
82
  "timer-node": "^5.0.7",
82
83
  "uuid": "^9.0.0",
@@ -85,5 +86,5 @@
85
86
  "publishConfig": {
86
87
  "access": "public"
87
88
  },
88
- "gitHead": "253890f0a88d586c4d023c6ace7a5fd3d5085568"
89
+ "gitHead": "ff65fa7623e6793f308b61d6ad734eedf3c87e2b"
89
90
  }
@@ -0,0 +1,155 @@
1
+ import dotenv from "dotenv"
2
+ import * as functions from "firebase-functions"
3
+ import { ApiSdk } from "@bandada/api-sdk"
4
+ import { groth16 } from "snarkjs"
5
+ import { getAuth } from "firebase-admin/auth"
6
+ import admin from "firebase-admin"
7
+ import { BandadaValidateProof, VerifiedBandadaResponse } from "../types/index"
8
+
9
+ const VKEY_DATA = {
10
+ protocol: "groth16",
11
+ curve: "bn128",
12
+ nPublic: 3,
13
+ vk_alpha_1: [
14
+ "20491192805390485299153009773594534940189261866228447918068658471970481763042",
15
+ "9383485363053290200918347156157836566562967994039712273449902621266178545958",
16
+ "1"
17
+ ],
18
+ vk_beta_2: [
19
+ [
20
+ "6375614351688725206403948262868962793625744043794305715222011528459656738731",
21
+ "4252822878758300859123897981450591353533073413197771768651442665752259397132"
22
+ ],
23
+ [
24
+ "10505242626370262277552901082094356697409835680220590971873171140371331206856",
25
+ "21847035105528745403288232691147584728191162732299865338377159692350059136679"
26
+ ],
27
+ ["1", "0"]
28
+ ],
29
+ vk_gamma_2: [
30
+ [
31
+ "10857046999023057135944570762232829481370756359578518086990519993285655852781",
32
+ "11559732032986387107991004021392285783925812861821192530917403151452391805634"
33
+ ],
34
+ [
35
+ "8495653923123431417604973247489272438418190587263600148770280649306958101930",
36
+ "4082367875863433681332203403145435568316851327593401208105741076214120093531"
37
+ ],
38
+ ["1", "0"]
39
+ ],
40
+ vk_delta_2: [
41
+ [
42
+ "3697618915467790705869942236922063775466274665053173890632463796679068973252",
43
+ "14948341351907992175709156460547989243732741534604949238422596319735704165658"
44
+ ],
45
+ [
46
+ "3028459181652799888716942141752307629938889957960373621898607910203491239368",
47
+ "11380736494786911280692284374675752681598754560757720296073023058533044108340"
48
+ ],
49
+ ["1", "0"]
50
+ ],
51
+ vk_alphabeta_12: [
52
+ [
53
+ [
54
+ "2029413683389138792403550203267699914886160938906632433982220835551125967885",
55
+ "21072700047562757817161031222997517981543347628379360635925549008442030252106"
56
+ ],
57
+ [
58
+ "5940354580057074848093997050200682056184807770593307860589430076672439820312",
59
+ "12156638873931618554171829126792193045421052652279363021382169897324752428276"
60
+ ],
61
+ [
62
+ "7898200236362823042373859371574133993780991612861777490112507062703164551277",
63
+ "7074218545237549455313236346927434013100842096812539264420499035217050630853"
64
+ ]
65
+ ],
66
+ [
67
+ [
68
+ "7077479683546002997211712695946002074877511277312570035766170199895071832130",
69
+ "10093483419865920389913245021038182291233451549023025229112148274109565435465"
70
+ ],
71
+ [
72
+ "4595479056700221319381530156280926371456704509942304414423590385166031118820",
73
+ "19831328484489333784475432780421641293929726139240675179672856274388269393268"
74
+ ],
75
+ [
76
+ "11934129596455521040620786944827826205713621633706285934057045369193958244500",
77
+ "8037395052364110730298837004334506829870972346962140206007064471173334027475"
78
+ ]
79
+ ]
80
+ ],
81
+ IC: [
82
+ [
83
+ "12951059800758687233303204819298121944551181861362200875212570257618182506154",
84
+ "5751958719396509176593242305268064754837298673622815112953832050159760501392",
85
+ "1"
86
+ ],
87
+ [
88
+ "9561588427935871983444704959674198910445823619407211599507208879011862515257",
89
+ "14576201570478094842467636169770180675293504492823217349086195663150934064643",
90
+ "1"
91
+ ],
92
+ [
93
+ "4811967233483727873912563574622036989372099129165459921963463310078093941559",
94
+ "1874883809855039536107616044787862082553628089593740724610117059083415551067",
95
+ "1"
96
+ ],
97
+ [
98
+ "12252730267779308452229639835051322390696643456253768618882001876621526827161",
99
+ "7899194018737016222260328309937800777948677569409898603827268776967707173231",
100
+ "1"
101
+ ]
102
+ ]
103
+ }
104
+
105
+ dotenv.config()
106
+
107
+ const { BANDADA_API_URL, BANDADA_GROUP_ID } = process.env
108
+
109
+ const bandadaApi = new ApiSdk(BANDADA_API_URL)
110
+
111
+ export const bandadaValidateProof = functions
112
+ .region("europe-west1")
113
+ .runWith({
114
+ memory: "512MB"
115
+ })
116
+ .https.onCall(async (data: BandadaValidateProof): Promise<VerifiedBandadaResponse> => {
117
+ if (!BANDADA_GROUP_ID) throw new Error("BANDADA_GROUP_ID is not defined in .env")
118
+
119
+ const { proof, publicSignals } = data
120
+ const isCorrect = groth16.verify(VKEY_DATA, publicSignals, proof)
121
+ if (!isCorrect)
122
+ return {
123
+ valid: false,
124
+ message: "Invalid proof",
125
+ token: ""
126
+ }
127
+
128
+ const commitment = data.publicSignals[1]
129
+ const isMember = await bandadaApi.isGroupMember(BANDADA_GROUP_ID, commitment)
130
+ if (!isMember)
131
+ return {
132
+ valid: false,
133
+ message: "Not a member of the group",
134
+ token: ""
135
+ }
136
+ const auth = getAuth()
137
+ try {
138
+ await admin.auth().createUser({
139
+ uid: commitment
140
+ })
141
+ } catch (error: any) {
142
+ // if user already exist then just pass
143
+ if (error.code !== "auth/uid-already-exists") {
144
+ throw new Error(error)
145
+ }
146
+ }
147
+ const token = await auth.createCustomToken(commitment)
148
+ return {
149
+ valid: true,
150
+ message: "Valid proof and group member",
151
+ token
152
+ }
153
+ })
154
+
155
+ export default bandadaValidateProof
@@ -31,6 +31,8 @@ export {
31
31
  generatePreSignedUrlsParts,
32
32
  completeMultiPartUpload
33
33
  } from "./storage"
34
+ export { bandadaValidateProof } from "./bandada"
35
+ export { checkNonceOfSIWEAddress } from "./siwe"
34
36
  export { checkAndRemoveBlockingContributor, resumeContributionAfterTimeoutExpiration } from "./timeout"
35
37
 
36
38
  admin.initializeApp()
@@ -0,0 +1,77 @@
1
+ import dotenv from "dotenv"
2
+ import fetch from "@adobe/node-fetch-retry"
3
+ import * as functions from "firebase-functions"
4
+ import { getAuth } from "firebase-admin/auth"
5
+ import admin from "firebase-admin"
6
+ import { Auth0UserInfo, CheckNonceOfSIWEAddressRequest, CheckNonceOfSIWEAddressResponse } from "../types"
7
+ import { setEthProvider } from "../lib/services"
8
+
9
+ dotenv.config()
10
+
11
+ export const checkNonceOfSIWEAddress = functions
12
+ .region("europe-west1")
13
+ .runWith({ memory: "1GB" })
14
+ .https.onCall(async (data: CheckNonceOfSIWEAddressRequest): Promise<CheckNonceOfSIWEAddressResponse> => {
15
+ try {
16
+ const { auth0Token } = data
17
+ const result = (await fetch(`${process.env.AUTH0_APPLICATION_URL}/userinfo`, {
18
+ method: "GET",
19
+ headers: {
20
+ "content-type": "application/json",
21
+ authorization: `Bearer ${auth0Token}`
22
+ }
23
+ }).then((_res) => _res.json())) as Auth0UserInfo
24
+ if (!result.sub) {
25
+ return {
26
+ valid: false,
27
+ message: "No user detected. Please check device flow token"
28
+ }
29
+ }
30
+ const auth = getAuth()
31
+ // check nonce
32
+ const parts = result.sub.split("|")
33
+ const address = decodeURIComponent(parts[2]).split("eip155:534352:")[1]
34
+
35
+ const minimumNonce = Number(process.env.ETH_MINIMUM_NONCE)
36
+ const nonceBlockHeight = "latest" // process.env.ETH_NONCE_BLOCK_HEIGHT
37
+ // look up nonce for address @block
38
+ let nonceOk = true
39
+ if (minimumNonce > 0) {
40
+ const provider = setEthProvider()
41
+ console.log(`got provider - block # ${await provider.getBlockNumber()}`)
42
+ const nonce = await provider.getTransactionCount(address, nonceBlockHeight)
43
+ console.log(`nonce ${nonce}`)
44
+ nonceOk = nonce >= minimumNonce
45
+ }
46
+ console.log(`checking nonce ${nonceOk}`)
47
+ if (!nonceOk) {
48
+ return {
49
+ valid: false,
50
+ message: "Eth address does not meet the nonce requirements"
51
+ }
52
+ }
53
+ try {
54
+ await admin.auth().createUser({
55
+ displayName: address,
56
+ uid: address
57
+ })
58
+ } catch (error: any) {
59
+ // if user already exist then just pass
60
+ if (error.code !== "auth/uid-already-exists") {
61
+ throw new Error(error)
62
+ }
63
+ }
64
+ const token = await auth.createCustomToken(address)
65
+ return {
66
+ valid: true,
67
+ token
68
+ }
69
+ } catch (error) {
70
+ return {
71
+ valid: false,
72
+ message: `Something went wrong ${error}`
73
+ }
74
+ }
75
+ })
76
+
77
+ export default checkNonceOfSIWEAddress
@@ -55,7 +55,7 @@ export const registerAuthUser = functions
55
55
  ) {
56
56
  const auth = admin.auth()
57
57
  // if provider == github.com let's use our functions to check the user's reputation
58
- if (user.providerData[0].providerId === "github.com") {
58
+ if (user.providerData.length > 0 && user.providerData[0].providerId === "github.com") {
59
59
  const vars = getGitHubVariables()
60
60
 
61
61
  // this return true or false
@@ -112,7 +112,7 @@ export const registerAuthUser = functions
112
112
  encodedDisplayName,
113
113
  // Metadata.
114
114
  creationTime,
115
- lastSignInTime,
115
+ lastSignInTime: lastSignInTime || creationTime,
116
116
  // Optional.
117
117
  email: email || "",
118
118
  emailVerified: emailVerified || false,
package/src/lib/errors.ts CHANGED
@@ -7,7 +7,7 @@ import { LogLevel } from "../types/enums"
7
7
  * @notice the set of Firebase Functions status codes. The codes are the same at the
8
8
  * ones exposed by {@link https://github.com/grpc/grpc/blob/master/doc/statuscodes.md | gRPC}.
9
9
  * @param errorCode <FunctionsErrorCode> - the set of possible error codes.
10
- * @param message <string> - the error messge.
10
+ * @param message <string> - the error message.
11
11
  * @param [details] <string> - the details of the error (optional).
12
12
  * @returns <HttpsError>
13
13
  */
@@ -1,6 +1,11 @@
1
+ import dotenv from "dotenv"
2
+ import ethers from "ethers"
1
3
  import { S3Client } from "@aws-sdk/client-s3"
2
4
  import { COMMON_ERRORS, logAndThrowError } from "./errors"
3
5
 
6
+ dotenv.config()
7
+ let provider: ethers.providers.Provider
8
+
4
9
  /**
5
10
  * Return a configured and connected instance of the AWS S3 client.
6
11
  * @dev this method check and utilize the environment variables to configure the connection
@@ -26,3 +31,34 @@ export const getS3Client = async (): Promise<S3Client> => {
26
31
  region: process.env.AWS_REGION!
27
32
  })
28
33
  }
34
+
35
+ /**
36
+ * Returns a Prvider, connected via a configured JSON URL or else
37
+ * the ethers.js default provider, using configured API keys.
38
+ * @returns <ethers.providers.Provider> An Eth node provider
39
+ */
40
+ export const setEthProvider = (): ethers.providers.Provider => {
41
+ if (provider) return provider
42
+ console.log(`setting new provider`)
43
+
44
+ // Use JSON URL if defined
45
+ // if ((hardhat as any).ethers) {
46
+ // console.log(`using hardhat.ethers provider`)
47
+ // provider = (hardhat as any).ethers.provider
48
+ // } else
49
+ if (process.env.ETH_PROVIDER_JSON_URL) {
50
+ console.log(`JSON URL provider at ${process.env.ETH_PROVIDER_JSON_URL}`)
51
+ provider = new ethers.providers.JsonRpcProvider({
52
+ url: process.env.ETH_PROVIDER_JSON_URL,
53
+ skipFetchSetup: true
54
+ })
55
+ } else {
56
+ // Otherwise, connect the default provider with ALchemy, Infura, or both
57
+ provider = ethers.providers.getDefaultProvider("homestead", {
58
+ alchemy: process.env.ETH_PROVIDER_ALCHEMY_API_KEY!,
59
+ infura: process.env.ETH_PROVIDER_INFURA_API_KEY!
60
+ })
61
+ }
62
+
63
+ return provider
64
+ }
@@ -0,0 +1 @@
1
+ declare module "@bandada/api-sdk"
@@ -1,4 +1,5 @@
1
1
  import { CeremonyInputData, CircuitDocument, ETagWithPartNumber } from "@devtion/actions"
2
+ import type { Groth16Proof, PublicSignals } from "snarkjs"
2
3
 
3
4
  /**
4
5
  * Group all the necessary data needed for running the `setupCeremony` cloud function.
@@ -138,3 +139,62 @@ export type FinalizeCircuitData = {
138
139
  bucketName: string
139
140
  beacon: string
140
141
  }
142
+
143
+ /**
144
+ * Group all the necessary data needed for running the `bandadaValidateProof` cloud function.
145
+ * @typedef {Object} BandadaValidateProof
146
+ * @property {string} merkleTreeRoot - the merkle tree root of the group.
147
+ * @property {string} nullifierHash - the nullifier hash of the member.
148
+ * @property {string} externalNullifier - the external nullifier of the member.
149
+ * @property {PackedProof} proof - the packed proof generated on the client.
150
+ */
151
+ export type BandadaValidateProof = {
152
+ proof: Groth16Proof
153
+ publicSignals: PublicSignals
154
+ }
155
+
156
+ /**
157
+ * Define the return object of the function that verifies the Bandada membership and proof.
158
+ * @typedef {Object} VerifiedBandadaResponse
159
+ * @property {boolean} valid - true if the proof is valid and the user is a member of the group; otherwise false.
160
+ * @property {string} message - a message describing the result of the verification.
161
+ * @property {string} token - the custom access token.
162
+ */
163
+ export type VerifiedBandadaResponse = {
164
+ valid: boolean
165
+ message: string
166
+ token: string
167
+ }
168
+
169
+ /**
170
+ * Define the check nonce object for the cloud function
171
+ * @typedef {Object} CheckNonceOfSIWEAddressRequest
172
+ * @property {string} auth0Token - token from the device flow authentication
173
+ */
174
+ export type CheckNonceOfSIWEAddressRequest = {
175
+ auth0Token: string
176
+ }
177
+
178
+ /**
179
+ * Define the check nonce response object of the cloud function
180
+ * @typedef {Object} CheckNonceOfSIWEAddressResponse
181
+ * @property {boolean} valid - if the checking result was valid or not
182
+ * @property {string} message - informative message
183
+ * @property {string} token - token to sign in
184
+ */
185
+ export type CheckNonceOfSIWEAddressResponse = {
186
+ valid: boolean
187
+ message?: string
188
+ token?: string
189
+ }
190
+ /**
191
+ * Define the response from auth0 /userinfo endpoint
192
+ *
193
+ */
194
+ export type Auth0UserInfo = {
195
+ sub: string
196
+ nickname: string
197
+ name: string
198
+ picture: string
199
+ updated_at: string
200
+ }