Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initialize Firestore on App.tsx #176

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/near-fast-auth-signer/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import VerifyEmailPage from './components/VerifyEmail/verify-email';
import FastAuthController from './lib/controller';
import './styles/theme.css';
import './styles/globals.css';
import FirestoreController from './lib/firestoreController';
import GlobalStyle from './styles/index';
import { basePath, networkId } from './utils/config';

Expand Down Expand Up @@ -59,7 +60,10 @@ export default function App() {
log('faLog');
log2('faLogzzzzz');

// @ts-ignore
if (!window.firestoreController) {
window.firestoreController = new FirestoreController();
}

return (
<>
<GlobalStyle />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,6 @@ function SignInPage() {
const skipGetKey = decodeIfTruthy(searchParams.get('skipGetKey'));
const { authenticated } = useAuthState(skipGetKey);
const [renderRedirectButton, setRenderRedirectButton] = useState('');
if (!window.firestoreController) {
window.firestoreController = new FirestoreController();
}

const addDevice = useCallback(async (data: any) => {
if (!data.email) return;
Expand Down Expand Up @@ -177,9 +174,8 @@ function SignInPage() {
: null;
const existingDeviceLakKey = existingDevice?.publicKeys?.filter((key) => key !== publicKeyFak)[0];

// @ts-ignore
const oidcToken = user.accessToken;
const recoveryPK = await window.fastAuthController.getUserCredential(oidcToken);
const recoveryPK = await window.fastAuthController
.getUserCredential(await FirestoreController.getUserOidcToken());

// if given lak key is already attached to webAuthN public key, no need to add it again
const noNeedToAddKey = existingDeviceLakKey === public_key;
Expand Down Expand Up @@ -209,13 +205,6 @@ function SignInPage() {
return null;
}

// Add device
window.firestoreController.updateUser({
userUid: user.uid,
// User type is missing accessToken but it exist
oidcToken,
});

// Since FAK is already added, we only add LAK
return window.firestoreController.addDeviceCollection({
fakPublicKey: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import styled from 'styled-components';

import { createNEARAccount, fetchAccountIds } from '../../api';
import FastAuthController from '../../lib/controller';
import FirestoreController from '../../lib/firestoreController';
import {
decodeIfTruthy, inIframe, isUrlNotJavascriptProtocol, redirectWithError
} from '../../utils';
Expand Down Expand Up @@ -207,10 +206,6 @@ function AuthCallbackPage() {
window.location.replace(parsedUrl.href);
}

if (!window.firestoreController) {
window.firestoreController = new FirestoreController();
}

setStatusMessage('Verifying email...');

try {
Expand Down Expand Up @@ -241,11 +236,6 @@ function AuthCallbackPage() {

await window.fastAuthController.claimOidcToken(accessToken);
const oidcKeypair = await window.fastAuthController.getKey(`oidc_keypair_${accessToken}`);
window.firestoreController = new FirestoreController();
window.firestoreController.updateUser({
userUid: user.uid,
oidcToken: accessToken,
});

const callback = isRecovery ? onSignIn : onCreateAccount;
await callback({
Expand Down
57 changes: 32 additions & 25 deletions packages/near-fast-auth-signer/src/components/Devices/Devices.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { captureException } from '@sentry/react';
import React, { useEffect, useMemo, useState } from 'react';
import React, { useEffect, useState } from 'react';
import { useSearchParams, useNavigate } from 'react-router-dom';
import { styled } from 'styled-components';

Expand Down Expand Up @@ -48,7 +48,6 @@ function Devices() {
const [isAddingKey, setIsAddingKey] = useState(false);
const [isVerifyEmailRequired, setVerifyEmailRequired] = useState(false);
const [deleteCollections, setDeleteCollections] = useState([]);
const controller = useMemo(() => new FirestoreController(), []);
const [searchParams] = useSearchParams();
const navigate = useNavigate();
const public_key_lak = decodeIfTruthy(searchParams.get('public_key_lak')) || decodeIfTruthy(searchParams.get('public_key'));
Expand All @@ -64,34 +63,42 @@ function Devices() {

useEffect(() => {
const getCollection = async () => {
const deviceCollections = await controller.listDevices();
const deviceCollections = await window.firestoreController.listDevices();
setIsLoaded(false);
setCollections(deviceCollections);
};

const getKeypairOrLogout = () => window.fastAuthController.findInKeyStores(`oidc_keypair_${controller.getUserOidcToken()}`).then((keypair) => {
if (keypair) {
getCollection();
} else {
window.fastAuthController.clearUser().then(() => {
setVerifyEmailRequired(true);
setIsLoaded(false);
});
}
});
setIsLoaded(true);
if (controller.getUserOidcToken()) {
getKeypairOrLogout();
} else {
(new Promise((resolve) => { setTimeout(resolve, 5000); })).then(controller.getUserOidcToken).then((token) => {
if (!token) {
setVerifyEmailRequired(true);
const getKeypairOrLogout = async () => window.fastAuthController
.findInKeyStores(`oidc_keypair_${await FirestoreController.getUserOidcToken()}`)
.then((keypair) => {
if (keypair) {
getCollection();
} else {
getKeypairOrLogout();
window.fastAuthController.clearUser().then(() => {
setVerifyEmailRequired(true);
setIsLoaded(false);
});
}
});
}
}, [controller]);
setIsLoaded(true);

const verifyUserAuthenticationStatus = async () => {
if (await FirestoreController.getUserOidcToken()) {
getKeypairOrLogout();
} else {
(new Promise((resolve) => { setTimeout(resolve, 5000); }))
.then(FirestoreController.getUserOidcToken).then((token) => {
if (!token) {
setVerifyEmailRequired(true);
} else {
getKeypairOrLogout();
}
});
}
};

verifyUserAuthenticationStatus();
}, []);

const redirectToSignin = () => {
if (inIframe()) {
Expand All @@ -115,7 +122,7 @@ function Devices() {
};
});

return controller.deleteDeviceCollections(list)
return window.firestoreController.deleteDeviceCollections(list)
.then(async () => {
setisDeleted(false);
setCollections(collections.filter((collection) => (!deleteCollections.includes(collection.id))));
Expand All @@ -128,7 +135,7 @@ function Devices() {
const email = window.localStorage.getItem('emailForSignIn');
const methodNames = decodeIfTruthy(searchParams.get('methodNames'));
const success_url = decodeIfTruthy(searchParams.get('success_url'));
const oidcToken = await controller.getUserOidcToken();
const oidcToken = await FirestoreController.getUserOidcToken();
await onSignIn({
accessToken: oidcToken,
publicKeyFak,
Expand Down
4 changes: 1 addition & 3 deletions packages/near-fast-auth-signer/src/lib/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,6 @@ class FastAuthController {
});
} catch {
// fallback, non webAuthN supported browser
// @ts-ignore
const oidcToken = await firebaseAuth.currentUser.getIdToken();
const recoveryPK = await this.getUserCredential(oidcToken);
// make sure to handle failure, (eg token expired) if fail, redirect to failure_url
Expand Down Expand Up @@ -284,8 +283,7 @@ class FastAuthController {
}
}

async getUserCredential(oidcToken) {
// @ts-ignore
async getUserCredential(oidcToken: string) {
const GET_USER_SALT = CLAIM + 2;
const keypair = await this.getKey(`oidc_keypair_${oidcToken}`) || await this.getLocalStoreKey(`oidc_keypair_${oidcToken}`);

Expand Down
59 changes: 19 additions & 40 deletions packages/near-fast-auth-signer/src/lib/firestoreController.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,24 @@
import { captureException } from '@sentry/react';
import { User } from 'firebase/auth';
import {
getFirestore, Firestore, collection, setDoc, getDoc, getDocs, query, doc, CollectionReference,
deleteDoc,
} from 'firebase/firestore';
import UAParser from 'ua-parser-js';

import { fetchAccountIds } from '../api';
import { checkFirestoreReady, firebaseApp, firebaseAuth } from '../utils/firebase';
import { firebaseApp, firebaseAuth } from '../utils/firebase';
import { getDeleteKeysAction } from '../utils/mpc-service';
import { Device } from '../utils/types';

class FirestoreController {
private firestore: Firestore;

private userUid: string;

private oidcToken: string;

constructor() {
this.firestore = getFirestore(firebaseApp);

firebaseAuth.onIdTokenChanged(async (user: User) => {
if (!user) {
return;
}
this.userUid = user.uid;
this.oidcToken = await user.getIdToken();
});

checkFirestoreReady();
}

async getAccountIdFromOidcToken() {
const recoveryPK = await window.fastAuthController.getUserCredential(this.oidcToken);
static async getAccountIdFromOidcToken() {
const recoveryPK = await window.fastAuthController.getUserCredential(await firebaseAuth.currentUser?.getIdToken());
const accountIds = await fetchAccountIds(recoveryPK);

if (!accountIds.length) {
Expand Down Expand Up @@ -69,12 +54,12 @@ class FirestoreController {
}

if (fakPublicKey) {
const fakDoc = setDoc(doc(this.firestore, `/users/${this.userUid}/devices`, fakPublicKey), {
const fakDoc = setDoc(doc(this.firestore, `/users/${firebaseAuth.currentUser?.uid}/devices`, fakPublicKey), {
device: `${device.vendor} ${device.model}`,
os: `${os.name} ${os.version}`,
browser: `${browser.name} ${browser.version}`,
publicKeys: [fakPublicKey],
uid: this.userUid,
uid: firebaseAuth.currentUser?.uid,
gateway: gateway || 'Unknown Gateway',
dateTime,
keyType: 'fak',
Expand All @@ -83,12 +68,12 @@ class FirestoreController {
}

if (lakPublicKey) {
const lakDoc = setDoc(doc(this.firestore, `/users/${this.userUid}/devices`, lakPublicKey), {
const lakDoc = setDoc(doc(this.firestore, `/users/${firebaseAuth.currentUser?.uid}/devices`, lakPublicKey), {
device: `${device.vendor} ${device.model}`,
os: `${os.name} ${os.version}`,
browser: `${browser.name} ${browser.version}`,
publicKeys: [lakPublicKey],
uid: this.userUid,
uid: firebaseAuth.currentUser?.uid,
gateway: gateway || 'Unknown Gateway',
dateTime,
keyType: 'lak',
Expand All @@ -104,7 +89,7 @@ class FirestoreController {
}

async listDevices() {
const q = query(collection(this.firestore, `/users/${this.userUid}/devices`) as CollectionReference<Device>);
const q = query(collection(this.firestore, `/users/${firebaseAuth.currentUser?.uid}/devices`) as CollectionReference<Device>);
const querySnapshot = await getDocs(q);
const collections = [];

Expand All @@ -119,18 +104,18 @@ class FirestoreController {
});
});

const existingKeyPair = window.fastAuthController.findInKeyStores(`oidc_keypair_${this.oidcToken}`);
const existingKeyPair = window.fastAuthController.findInKeyStores(`oidc_keypair_${await firebaseAuth.currentUser?.getIdToken()}`);
if (!existingKeyPair) {
await (window as any).fastAuthController.claimOidcToken(this.oidcToken);
await window.fastAuthController.claimOidcToken(await firebaseAuth.currentUser?.getIdToken());
}

if (!window.fastAuthController.getAccountId()) {
const accountId = await this.getAccountIdFromOidcToken();
const accountId = await FirestoreController.getAccountIdFromOidcToken();
window.fastAuthController.setAccountId(accountId);
}

const accessKeysWithoutRecoveryKey = await window.fastAuthController
.getAllAccessKeysExceptRecoveryKey(this.oidcToken);
.getAllAccessKeysExceptRecoveryKey(await firebaseAuth.currentUser?.getIdToken());

// TODO: from the list, exclude record that has same key from recovery service
return accessKeysWithoutRecoveryKey.reduce((list, key) => {
Expand All @@ -154,7 +139,7 @@ class FirestoreController {
}

async deleteDeviceCollections(list) {
const recoveryPK = await window.fastAuthController.getUserCredential(this.oidcToken);
const recoveryPK = await window.fastAuthController.getUserCredential(await firebaseAuth.currentUser?.getIdToken());
const accountIds = await fetchAccountIds(recoveryPK);

// delete firebase records
Expand All @@ -165,7 +150,7 @@ class FirestoreController {
if (firestoreIds.length) {
// delete all records except the one that has LAK
const deletePromises = firestoreIds.flatMap((id) => [
deleteDoc(doc(this.firestore, `/users/${this.userUid}/devices`, id)),
deleteDoc(doc(this.firestore, `/users/${firebaseAuth.currentUser?.uid}/devices`, id)),
deleteDoc(doc(this.firestore, '/publicKeys', id))
]);
await Promise.allSettled(deletePromises);
Expand All @@ -180,7 +165,7 @@ class FirestoreController {
const publicKeys = list.reduce((acc, curr) => acc.concat(curr.publicKeys), []);
const deleteAction = getDeleteKeysAction(publicKeys);
await (window as any).fastAuthController.signAndSendActionsWithRecoveryKey({
oidcToken: this.oidcToken,
oidcToken: await firebaseAuth.currentUser?.getIdToken(),
accountId: accountIds[0],
recoveryPK,
actions: deleteAction
Expand All @@ -192,7 +177,7 @@ class FirestoreController {
}

async getDeviceCollection(fakPublicKey) {
const docRef = doc(this.firestore, 'users', this.userUid, 'devices', fakPublicKey);
const docRef = doc(this.firestore, 'users', firebaseAuth.currentUser?.uid, 'devices', fakPublicKey);
const docSnap = await getDoc(docRef);

if (docSnap.exists()) {
Expand All @@ -201,15 +186,9 @@ class FirestoreController {
return null;
}

updateUser = async ({
userUid,
oidcToken,
}) => {
this.userUid = userUid;
this.oidcToken = oidcToken;
};

getUserOidcToken = () => this.oidcToken;
static async getUserOidcToken() {
return firebaseAuth.currentUser?.getIdToken();
}

async addAccountIdPublicKey(publicKey: string, accountId: string) {
await setDoc(doc(this.firestore, 'publicKeys', publicKey), {
Expand Down
Loading