diff --git a/package.json b/package.json
index 3ebd2e1..129a2db 100644
--- a/package.json
+++ b/package.json
@@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"homepage": ".",
+ "proxy": "https://ccv-pubs.web.app/",
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
diff --git a/src/components/ContentPage.jsx b/src/components/ContentPage.jsx
index 2e1af69..02a1939 100755
--- a/src/components/ContentPage.jsx
+++ b/src/components/ContentPage.jsx
@@ -14,7 +14,7 @@ export function ContentPage() {
return (
- {user ? (
+ {user?.ccv ? (
diff --git a/src/utils/firebase.ts b/src/utils/firebase.ts
index f510031..21274c5 100644
--- a/src/utils/firebase.ts
+++ b/src/utils/firebase.ts
@@ -4,6 +4,7 @@ import { initializeApp } from 'firebase/app';
import {
getFirestore,
doc,
+ getDoc,
setDoc,
collection,
onSnapshot,
@@ -19,7 +20,8 @@ import {
signOut,
} from 'firebase/auth';
-import { setPublications, setUser } from '../store/slice/appState';
+import { setPublications, setUser as setUserState } from '../store/slice/appState';
+import { User } from '../../types';
const firebaseConfig = {
apiKey: 'AIzaSyBlu1GzA5jvM6mh6taIcjtNgcSEVxlxa1Q',
@@ -35,15 +37,50 @@ const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
const auth = getAuth();
const provider = new GoogleAuthProvider();
+provider.addScope('https://www.googleapis.com/auth/cloud-identity.groups.readonly');
provider.setCustomParameters({
hd: 'brown.edu',
});
-const collectionName = 'publications';
+const publicationsCollection = 'publications';
+const usersCollection = 'users';
+const ccvStaffGoogleGroupId = '0184mhaj2thv9r6';
export const handleLogin = async () => {
try {
- await signInWithPopup(auth, provider);
+ const result = await signInWithPopup(auth, provider);
+ const credential = GoogleAuthProvider.credentialFromResult(result);
+ const token = credential.accessToken;
+ const { displayName, email } = result.user;
+
+ // Fetch members from the CCV group
+ // If the user is in the group, we'll get back members. Otherwise, we'll get a permission error
+ // The access token is in scope here and I didn't want to save it anywhere
+ const response = await fetch(
+ `https://cloudidentity.googleapis.com/v1/groups/${ccvStaffGoogleGroupId}/memberships`,
+ {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ }
+ );
+
+ let ccv = false;
+ if (response.ok) {
+ const { memberships } = (await response.json()) ?? [];
+
+ // Double check that the user is a member in the ccv group
+ ccv = memberships.some((m) => m.preferredMemberKey.id.toLowerCase() === email.toLowerCase());
+ }
+
+ const userDoc = await getUser(email);
+ if (!userDoc || userDoc.ccv !== ccv || userDoc.displayName !== displayName) {
+ await updateUser({
+ displayName,
+ email,
+ ccv,
+ });
+ }
} catch (error) {
console.log(error);
}
@@ -64,24 +101,22 @@ export const useAuthStateChanged = () => {
const dispatch = useDispatch();
useEffect(() => {
- const unsubscribe = onAuthStateChanged(auth, (user) => {
+ const unsubscribeAuth = onAuthStateChanged(auth, async (user) => {
if (user) {
- const { displayName, email, uid } = user;
-
- dispatch(
- setUser({
- displayName,
- email,
- uid,
- })
- );
+ const { email } = user;
+ const unsubscribeUser = onSnapshot(doc(db, usersCollection, email), (doc) => {
+ if (doc.exists) {
+ dispatch(setUserState(doc.data()));
+ }
+ });
+ return () => unsubscribeUser();
} else {
- dispatch(setUser(null));
+ dispatch(setUserState(null));
}
});
return () => {
- unsubscribe();
+ unsubscribeAuth();
};
}, [dispatch]);
};
@@ -95,7 +130,7 @@ export const usePublicationsCollection = () => {
useEffect(() => {
const unsubscribe = onSnapshot(
query(
- collection(db, collectionName),
+ collection(db, publicationsCollection),
orderBy('updatedAt', 'desc'),
limit(100) // TODO: TEMPORARY. Limiting right now. Set up pagination?
),
@@ -116,10 +151,28 @@ export const usePublicationsCollection = () => {
export const addPublication = async (publication) => {
const docId = publication.doi.toLowerCase().replace(/\//g, '_');
- const docRef = doc(db, collectionName, docId);
+ const docRef = doc(db, publicationsCollection, docId);
await setDoc(docRef, {
...publication,
updatedAt: Date.now(),
});
};
+
+const updateUser = async ({ displayName, email, ccv }) => {
+ const docRef = doc(db, usersCollection, email);
+
+ await setDoc(docRef, {
+ displayName,
+ email,
+ ccv,
+ updatedAt: Date.now(),
+ });
+};
+
+const getUser = async (email): Promise
=> {
+ const docRef = doc(db, usersCollection, email);
+ const docSnap = await getDoc(docRef);
+
+ return docSnap.data() as User;
+};
diff --git a/types/index.d.ts b/types/index.d.ts
index b4f79f8..15c8ef4 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -8,3 +8,10 @@ export interface Publication {
year: number;
abstract: string;
}
+
+export interface User {
+ displayName: string;
+ email: string;
+ ccv: boolean;
+ updatedAt: number;
+}