From addeb814cc428d1fc61bf5d9c9e4836d5a02974b Mon Sep 17 00:00:00 2001 From: Luke Schlangen Date: Wed, 6 Sep 2023 10:09:14 -0500 Subject: [PATCH 1/3] feature: protect all routes with app check --- app-dev/party-game/app/actions/create-game.ts | 4 +++- app-dev/party-game/app/actions/delete-game.ts | 5 +++-- app-dev/party-game/app/actions/exit-game.ts | 5 +++-- app-dev/party-game/app/actions/join-game.ts | 5 +++-- app-dev/party-game/app/actions/nudge-game.ts | 9 +++++++-- app-dev/party-game/app/actions/start-game.ts | 5 +++-- app-dev/party-game/app/actions/update-answer.ts | 5 +++-- .../app/components/border-countdown-timer.tsx | 11 ++++++++++- .../party-game/app/components/create-game-form.tsx | 12 ++++++++++-- .../app/components/delete-game-button.tsx | 8 ++++++-- .../party-game/app/components/exit-game-button.tsx | 8 ++++++-- app-dev/party-game/app/components/game-list.tsx | 2 +- app-dev/party-game/app/components/lobby.tsx | 2 +- .../party-game/app/components/question-panel.tsx | 10 +++++++--- app-dev/party-game/app/components/scoreboard.tsx | 2 +- .../party-game/app/components/start-game-button.tsx | 6 +++++- app-dev/party-game/app/hooks/use-game.ts | 10 ++++++---- .../app/lib/firebase-client-initialization.ts | 13 +++++++++++++ app-dev/party-game/app/lib/firebase-config.ts | 1 + app-dev/party-game/postcss.config.js | 2 +- 20 files changed, 93 insertions(+), 32 deletions(-) diff --git a/app-dev/party-game/app/actions/create-game.ts b/app-dev/party-game/app/actions/create-game.ts index 1393aa05..e6d1608c 100644 --- a/app-dev/party-game/app/actions/create-game.ts +++ b/app-dev/party-game/app/actions/create-game.ts @@ -22,8 +22,10 @@ import {Game, GameSettings, Question, QuestionSchema, gameStates} from '@/app/ty import {QueryDocumentSnapshot, Timestamp} from 'firebase-admin/firestore'; import {GameSettingsSchema} from '@/app/types'; import {getAuth} from 'firebase-admin/auth'; +import {getAppCheck} from 'firebase-admin/app-check'; -export async function createGameAction({gameSettings, token}: {gameSettings: GameSettings, token: string}): Promise<{gameId: string}> { +export async function createGameAction({gameSettings, token, appCheckToken}: {gameSettings: GameSettings, token: string, appCheckToken: string}): Promise<{gameId: string}> { + await getAppCheck().verifyToken(appCheckToken); const authUser = await getAuth(app).verifyIdToken(token); // Parse request (throw an error if not correct) diff --git a/app-dev/party-game/app/actions/delete-game.ts b/app-dev/party-game/app/actions/delete-game.ts index 2a5ded30..84ef4ec7 100644 --- a/app-dev/party-game/app/actions/delete-game.ts +++ b/app-dev/party-game/app/actions/delete-game.ts @@ -18,10 +18,11 @@ import {app, gamesRef} from '@/app/lib/firebase-server-initialization'; import {GameIdSchema} from '@/app/types'; +import {getAppCheck} from 'firebase-admin/app-check'; import {getAuth} from 'firebase-admin/auth'; -export async function deleteGameAction({gameId, token}: {gameId: string, token: string}) { - // Authenticate user +export async function deleteGameAction({gameId, token, appCheckToken}: {gameId: string, token: string, appCheckToken: string}) { + await getAppCheck().verifyToken(appCheckToken); const authUser = await getAuth(app).verifyIdToken(token); // Parse request (throw an error if not correct) diff --git a/app-dev/party-game/app/actions/exit-game.ts b/app-dev/party-game/app/actions/exit-game.ts index a8d65cab..2e200d5d 100644 --- a/app-dev/party-game/app/actions/exit-game.ts +++ b/app-dev/party-game/app/actions/exit-game.ts @@ -20,9 +20,10 @@ import {app, gamesRef} from '@/app/lib/firebase-server-initialization'; import {FieldValue} from 'firebase-admin/firestore'; import {GameIdSchema} from '@/app/types'; import {getAuth} from 'firebase-admin/auth'; +import {getAppCheck} from 'firebase-admin/app-check'; -export async function exitGameAction({gameId, token}: {gameId: string, token: string}) { - // Authenticate user +export async function exitGameAction({gameId, token, appCheckToken}: {gameId: string, token: string, appCheckToken: string}) { + await getAppCheck().verifyToken(appCheckToken); const authUser = await getAuth(app).verifyIdToken(token); // Parse request (throw an error if not correct) diff --git a/app-dev/party-game/app/actions/join-game.ts b/app-dev/party-game/app/actions/join-game.ts index 65647528..554932c9 100644 --- a/app-dev/party-game/app/actions/join-game.ts +++ b/app-dev/party-game/app/actions/join-game.ts @@ -19,10 +19,11 @@ import {app, gamesRef} from '@/app/lib/firebase-server-initialization'; import {generateName} from '@/app/lib/name-generator'; import {GameIdSchema} from '@/app/types'; +import {getAppCheck} from 'firebase-admin/app-check'; import {getAuth} from 'firebase-admin/auth'; -export async function joinGameAction({gameId, token}: {gameId: string, token: string}) { - // Authenticate user +export async function joinGameAction({gameId, token, appCheckToken}: {gameId: string, token: string, appCheckToken: string}) { + await getAppCheck().verifyToken(appCheckToken); const authUser = await getAuth(app).verifyIdToken(token); // Parse request (throw an error if not correct) diff --git a/app-dev/party-game/app/actions/nudge-game.ts b/app-dev/party-game/app/actions/nudge-game.ts index ba58ae70..8dd83289 100644 --- a/app-dev/party-game/app/actions/nudge-game.ts +++ b/app-dev/party-game/app/actions/nudge-game.ts @@ -16,11 +16,16 @@ 'use server'; -import {gamesRef} from '@/app/lib/firebase-server-initialization'; +import {app, gamesRef} from '@/app/lib/firebase-server-initialization'; import {GameIdSchema, gameStates} from '@/app/types'; +import {getAppCheck} from 'firebase-admin/app-check'; +import {getAuth} from 'firebase-admin/auth'; import {Timestamp} from 'firebase-admin/firestore'; -export async function nudgeGame({gameId}: {gameId: string}) { +export async function nudgeGameAction({gameId, token, appCheckToken}: {gameId: string, token: string, appCheckToken: string}) { + await getAppCheck().verifyToken(appCheckToken); + await getAuth(app).verifyIdToken(token); + // Validate request // Will throw an error if not a string GameIdSchema.parse(gameId); diff --git a/app-dev/party-game/app/actions/start-game.ts b/app-dev/party-game/app/actions/start-game.ts index 1ec3d232..ebd89fa8 100644 --- a/app-dev/party-game/app/actions/start-game.ts +++ b/app-dev/party-game/app/actions/start-game.ts @@ -20,9 +20,10 @@ import {app, gamesRef} from '@/app/lib/firebase-server-initialization'; import {GameIdSchema, gameStates} from '@/app/types'; import {FieldValue} from 'firebase-admin/firestore'; import {getAuth} from 'firebase-admin/auth'; +import {getAppCheck} from 'firebase-admin/app-check'; -export async function startGameAction({gameId, token}: {gameId: string, token: string}) { - // Authenticate user +export async function startGameAction({gameId, token, appCheckToken}: {gameId: string, token: string, appCheckToken: string}) { + await getAppCheck().verifyToken(appCheckToken); const authUser = await getAuth(app).verifyIdToken(token); // Parse request (throw an error if not correct) diff --git a/app-dev/party-game/app/actions/update-answer.ts b/app-dev/party-game/app/actions/update-answer.ts index f6248678..ec5ff09c 100644 --- a/app-dev/party-game/app/actions/update-answer.ts +++ b/app-dev/party-game/app/actions/update-answer.ts @@ -18,11 +18,12 @@ import {app, gamesRef} from '@/app/lib/firebase-server-initialization'; import {GameIdSchema, gameStates} from '@/app/types'; +import {getAppCheck} from 'firebase-admin/app-check'; import {getAuth} from 'firebase-admin/auth'; import {z} from 'zod'; -export async function updateAnswerAction({gameId, answerSelection, token}: {gameId: string, answerSelection: boolean[], token: string}) { - // Authenticate user +export async function updateAnswerAction({gameId, answerSelection, token, appCheckToken}: {gameId: string, answerSelection: boolean[], token: string, appCheckToken: string}) { + await getAppCheck().verifyToken(appCheckToken); const authUser = await getAuth(app).verifyIdToken(token); // Parse request (throw an error if not correct) diff --git a/app-dev/party-game/app/components/border-countdown-timer.tsx b/app-dev/party-game/app/components/border-countdown-timer.tsx index 06472b08..587e794d 100644 --- a/app-dev/party-game/app/components/border-countdown-timer.tsx +++ b/app-dev/party-game/app/components/border-countdown-timer.tsx @@ -20,7 +20,9 @@ import {Game, gameStates} from '@/app/types'; import {DocumentReference, Timestamp} from 'firebase/firestore'; import {useEffect, useState} from 'react'; import useFirebaseAuthentication from '@/app/hooks/use-firebase-authentication'; -import {nudgeGame} from '@/app/actions/nudge-game'; +import {nudgeGameAction} from '@/app/actions/nudge-game'; +import {appCheck} from '@/app/lib/firebase-client-initialization'; +import {getToken} from 'firebase/app-check'; export default function BorderCountdownTimer({game, children, gameRef}: { game: Game, children: React.ReactNode, gameRef: DocumentReference }) { const [timeLeft, setTimeLeft] = useState(game.timePerQuestion); @@ -31,6 +33,13 @@ export default function BorderCountdownTimer({game, children, gameRef}: { game: const isShowingCorrectAnswers = game.state === gameStates.SHOWING_CORRECT_ANSWERS; const timeToCountDown = isShowingCorrectAnswers ? game.timePerAnswer : game.timePerQuestion; + const nudgeGame = async ({gameId}: {gameId: string}) => { + const appCheckTokenResponse = await getToken(appCheck, false); + const appCheckToken = appCheckTokenResponse.token; + const token = await authUser.getIdToken(); + nudgeGameAction({gameId, token, appCheckToken}); + }; + useEffect(() => { // all times are in seconds unless noted as `InMillis` const timeElapsedInMillis = Timestamp.now().toMillis() - game.startTime.seconds * 1000; diff --git a/app-dev/party-game/app/components/create-game-form.tsx b/app-dev/party-game/app/components/create-game-form.tsx index 1492d6a2..8f1ef694 100644 --- a/app-dev/party-game/app/components/create-game-form.tsx +++ b/app-dev/party-game/app/components/create-game-form.tsx @@ -21,8 +21,10 @@ import {useRouter} from 'next/navigation'; import {useEffect, useState} from 'react'; import BigColorBorderButton from './big-color-border-button'; import {TimePerAnswerSchema, TimePerQuestionSchema} from '@/app/types'; -import {createGameAction} from '../actions/create-game'; +import {createGameAction} from '@/app/actions/create-game'; import {z} from 'zod'; +import {getToken} from 'firebase/app-check'; +import {appCheck} from '@/app/lib/firebase-client-initialization'; export default function CreateGameForm() { const authUser = useFirebaseAuthentication(); @@ -38,15 +40,21 @@ export default function CreateGameForm() { const router = useRouter(); const onCreateGameSubmit = async (event: React.FormEvent) => { event.preventDefault(); + const appCheckTokenResponse = await getToken(appCheck, false); + const appCheckToken = appCheckTokenResponse.token; const token = await authUser.getIdToken(); try { - const response = await createGameAction({gameSettings: {timePerQuestion, timePerAnswer}, token}); + const response = await createGameAction({gameSettings: {timePerQuestion, timePerAnswer}, token, appCheckToken}); router.push(`/game/${response.gameId}`); } catch (error) { setSubmissionErrorMessage('There was an error handling the request.'); } }; + useEffect(() => { + setSubmissionErrorMessage(''); + }, [timePerQuestion, timePerAnswer]); + useEffect(() => { try { TimePerQuestionSchema.parse(timePerQuestion); diff --git a/app-dev/party-game/app/components/delete-game-button.tsx b/app-dev/party-game/app/components/delete-game-button.tsx index 686e28cc..31a84202 100644 --- a/app-dev/party-game/app/components/delete-game-button.tsx +++ b/app-dev/party-game/app/components/delete-game-button.tsx @@ -17,14 +17,18 @@ 'use client'; import useFirebaseAuthentication from '@/app/hooks/use-firebase-authentication'; -import {deleteGameAction} from '../actions/delete-game'; +import {deleteGameAction} from '@/app/actions/delete-game'; +import {appCheck} from '@/app/lib/firebase-client-initialization'; +import {getToken} from 'firebase/app-check'; export default function DeleteGameButton({gameId}: { gameId: string }) { const authUser = useFirebaseAuthentication(); const onDeleteGameClick = async () => { + const appCheckTokenResponse = await getToken(appCheck, false); + const appCheckToken = appCheckTokenResponse.token; const token = await authUser.getIdToken(); - await deleteGameAction({gameId, token}); + await deleteGameAction({gameId, token, appCheckToken}); }; return ( diff --git a/app-dev/party-game/app/components/exit-game-button.tsx b/app-dev/party-game/app/components/exit-game-button.tsx index 054b8fa8..c9230d29 100644 --- a/app-dev/party-game/app/components/exit-game-button.tsx +++ b/app-dev/party-game/app/components/exit-game-button.tsx @@ -18,15 +18,19 @@ import useFirebaseAuthentication from '@/app/hooks/use-firebase-authentication'; import {useRouter} from 'next/navigation'; -import {exitGameAction} from '../actions/exit-game'; +import {exitGameAction} from '@/app/actions/exit-game'; +import {getToken} from 'firebase/app-check'; +import {appCheck} from '@/app/lib/firebase-client-initialization'; export default function ExitGameButton({gameId}: { gameId: string }) { const authUser = useFirebaseAuthentication(); const router = useRouter(); const onExitGameClick = async () => { + const appCheckTokenResponse = await getToken(appCheck, false); + const appCheckToken = appCheckTokenResponse.token; const token = await authUser.getIdToken(); - await exitGameAction({gameId, token}); + await exitGameAction({gameId, token, appCheckToken}); router.push('/'); }; diff --git a/app-dev/party-game/app/components/game-list.tsx b/app-dev/party-game/app/components/game-list.tsx index 1bbb8e32..08e08b41 100644 --- a/app-dev/party-game/app/components/game-list.tsx +++ b/app-dev/party-game/app/components/game-list.tsx @@ -17,7 +17,7 @@ 'use client'; import Link from 'next/link'; -import useActiveGameList from '../hooks/use-active-game-list'; +import useActiveGameList from '@/app/hooks/use-active-game-list'; export default function GameList() { const {activeGameList} = useActiveGameList(); diff --git a/app-dev/party-game/app/components/lobby.tsx b/app-dev/party-game/app/components/lobby.tsx index a99cf2ae..37041608 100644 --- a/app-dev/party-game/app/components/lobby.tsx +++ b/app-dev/party-game/app/components/lobby.tsx @@ -20,7 +20,7 @@ import StartGameButton from '@/app/components/start-game-button'; import DeleteGameButton from '@/app/components/delete-game-button'; import PlayerList from './player-list'; import {Game} from '@/app/types'; -import useFirebaseAuthentication from '../hooks/use-firebase-authentication'; +import useFirebaseAuthentication from '@/app/hooks/use-firebase-authentication'; import ShareLinkPanel from './share-link-panel'; import {useState} from 'react'; diff --git a/app-dev/party-game/app/components/question-panel.tsx b/app-dev/party-game/app/components/question-panel.tsx index 4ecc8a27..3a3ca963 100644 --- a/app-dev/party-game/app/components/question-panel.tsx +++ b/app-dev/party-game/app/components/question-panel.tsx @@ -23,8 +23,10 @@ import useFirebaseAuthentication from '@/app/hooks/use-firebase-authentication'; import QRCode from 'react-qr-code'; import {useEffect, useState} from 'react'; import Scoreboard from './scoreboard'; -import useScoreboard from '../hooks/use-scoreboard'; -import {updateAnswerAction} from '../actions/update-answer'; +import useScoreboard from '@/app/hooks/use-scoreboard'; +import {updateAnswerAction} from '@/app/actions/update-answer'; +import {appCheck} from '@/app/lib/firebase-client-initialization'; +import {getToken} from 'firebase/app-check'; export default function QuestionPanel({game, gameRef, currentQuestion}: { game: Game, gameRef: DocumentReference, currentQuestion: Question }) { const authUser = useFirebaseAuthentication(); @@ -56,8 +58,10 @@ export default function QuestionPanel({game, gameRef, currentQuestion}: { game: const startingAnswerSelection = isSingleAnswer ? emptyAnswerSelection : answerSelection; const newAnswerSelection: boolean[] = startingAnswerSelection.with(answerIndex, !answerSelection[answerIndex]); + const appCheckTokenResponse = await getToken(appCheck, false); + const appCheckToken = appCheckTokenResponse.token; const token = await authUser.getIdToken(); - await updateAnswerAction({gameId, answerSelection: newAnswerSelection, token}); + await updateAnswerAction({gameId, answerSelection: newAnswerSelection, token, appCheckToken}); } }; diff --git a/app-dev/party-game/app/components/scoreboard.tsx b/app-dev/party-game/app/components/scoreboard.tsx index f57549c9..b8e1960f 100644 --- a/app-dev/party-game/app/components/scoreboard.tsx +++ b/app-dev/party-game/app/components/scoreboard.tsx @@ -17,7 +17,7 @@ 'use client'; import '@/app/components/player-list.css'; -import useScoreboard from '../hooks/use-scoreboard'; +import useScoreboard from '@/app/hooks/use-scoreboard'; export default function Scoreboard() { const {currentPlayer, playerScores} = useScoreboard(); diff --git a/app-dev/party-game/app/components/start-game-button.tsx b/app-dev/party-game/app/components/start-game-button.tsx index 2eba5294..3414d3a9 100644 --- a/app-dev/party-game/app/components/start-game-button.tsx +++ b/app-dev/party-game/app/components/start-game-button.tsx @@ -20,12 +20,16 @@ import useFirebaseAuthentication from '@/app/hooks/use-firebase-authentication'; import './big-color-border-button.css'; import BigColorBorderButton from '@/app/components/big-color-border-button'; import {startGameAction} from '@/app/actions/start-game'; +import {getToken} from 'firebase/app-check'; +import {appCheck} from '@/app/lib/firebase-client-initialization'; export default function StartGameButton({gameId}: {gameId: string}) { const authUser = useFirebaseAuthentication(); const onStartGameClick = async () => { + const appCheckTokenResponse = await getToken(appCheck, false); + const appCheckToken = appCheckTokenResponse.token; const token = await authUser.getIdToken(); - await startGameAction({gameId, token}); + await startGameAction({gameId, token, appCheckToken}); }; return ( diff --git a/app-dev/party-game/app/hooks/use-game.ts b/app-dev/party-game/app/hooks/use-game.ts index a86efd7f..2ab2d636 100644 --- a/app-dev/party-game/app/hooks/use-game.ts +++ b/app-dev/party-game/app/hooks/use-game.ts @@ -15,12 +15,13 @@ */ import {useEffect, useState} from 'react'; -import {db} from '@/app/lib/firebase-client-initialization'; +import {appCheck, db} from '@/app/lib/firebase-client-initialization'; import {Game, GameSchema, emptyGame, gameStates} from '@/app/types'; import {doc, onSnapshot} from 'firebase/firestore'; import {usePathname} from 'next/navigation'; import useFirebaseAuthentication from './use-firebase-authentication'; -import {joinGameAction} from '../actions/join-game'; +import {joinGameAction} from '@/app/actions/join-game'; +import {getToken} from 'firebase/app-check'; const useGame = () => { const pathname = usePathname(); @@ -31,8 +32,10 @@ const useGame = () => { useEffect(() => { const joinGame = async () => { + const appCheckTokenResponse = await getToken(appCheck, false); + const appCheckToken = appCheckTokenResponse.token; const token = await authUser.getIdToken(); - joinGameAction({gameId, token}); + joinGameAction({gameId, token, appCheckToken}); }; if (game.leader.uid && authUser.uid && game.leader.uid !== authUser.uid) { joinGame(); @@ -48,7 +51,6 @@ const useGame = () => { const game = GameSchema.parse(doc.data()); setGame(game); } catch (error) { - console.log(error); setErrorMessage(`Game ${gameId} was not found.`); } }); diff --git a/app-dev/party-game/app/lib/firebase-client-initialization.ts b/app-dev/party-game/app/lib/firebase-client-initialization.ts index b85772d3..a6ec48ce 100644 --- a/app-dev/party-game/app/lib/firebase-client-initialization.ts +++ b/app-dev/party-game/app/lib/firebase-client-initialization.ts @@ -14,12 +14,25 @@ * limitations under the License. */ +'use client'; + import {initializeApp} from 'firebase/app'; import {getFirestore} from 'firebase/firestore'; import {getAuth} from 'firebase/auth'; import {firebaseConfig} from '@/app/lib/firebase-config'; +import {AppCheck, initializeAppCheck, ReCaptchaEnterpriseProvider} from 'firebase/app-check'; // Initialize Firebase export const app = initializeApp(firebaseConfig); export const db = getFirestore(app); export const auth = getAuth(app); +export let appCheck: AppCheck; + +if (typeof window !== 'undefined') { + // Create a ReCaptchaEnterpriseProvider instance using your reCAPTCHA Enterprise + // site key and pass it to initializeAppCheck(). + appCheck = initializeAppCheck(app, { + provider: new ReCaptchaEnterpriseProvider('6Lc3JP8nAAAAAPrX4s-HwUT8L-k_aMtbKGhJEq_0'), + isTokenAutoRefreshEnabled: true, // Set to true to allow auto-refresh. + }); +} diff --git a/app-dev/party-game/app/lib/firebase-config.ts b/app-dev/party-game/app/lib/firebase-config.ts index 2244fd52..0cce5527 100644 --- a/app-dev/party-game/app/lib/firebase-config.ts +++ b/app-dev/party-game/app/lib/firebase-config.ts @@ -17,4 +17,5 @@ export const firebaseConfig = { apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY || 'AIzaSyBr0i2bC9kdsdRVh-9pQ5yFOjxpweiTJrQ', projectId: process.env.NEXT_PUBLIC_GOOGLE_CLOUD_PROJECT || 'cloud-quiz-next', + appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID || '1:406096902405:web:7311c44c3657568af1df6c', }; diff --git a/app-dev/party-game/postcss.config.js b/app-dev/party-game/postcss.config.js index 0f5702dc..67136b0c 100644 --- a/app-dev/party-game/postcss.config.js +++ b/app-dev/party-game/postcss.config.js @@ -19,4 +19,4 @@ module.exports = { tailwindcss: {}, autoprefixer: {}, }, -} +}; From c7da6af3c7730dbe4177aa1995b4096789a147e7 Mon Sep 17 00:00:00 2001 From: Luke Schlangen Date: Wed, 6 Sep 2023 10:19:59 -0500 Subject: [PATCH 2/3] chore: update packages --- .../app/components/question-panel.tsx | 10 +- app-dev/party-game/package-lock.json | 144 +++++++++--------- app-dev/party-game/package.json | 11 +- 3 files changed, 86 insertions(+), 79 deletions(-) diff --git a/app-dev/party-game/app/components/question-panel.tsx b/app-dev/party-game/app/components/question-panel.tsx index 3a3ca963..f0d8fada 100644 --- a/app-dev/party-game/app/components/question-panel.tsx +++ b/app-dev/party-game/app/components/question-panel.tsx @@ -55,8 +55,14 @@ export default function QuestionPanel({game, gameRef, currentQuestion}: { game: const onAnswerClick = async (answerIndex: number) => { if (game.state === gameStates.AWAITING_PLAYER_ANSWERS && !isGameLeader) { // If the user is only supposed to pick one answer, clear the other answers first - const startingAnswerSelection = isSingleAnswer ? emptyAnswerSelection : answerSelection; - const newAnswerSelection: boolean[] = startingAnswerSelection.with(answerIndex, !answerSelection[answerIndex]); + const newAnswerSelection: boolean[] = answerSelection.map((currentValue, index) => { + // update the selection to true + if (index === answerIndex) return true; + // update other selections to false if there is only one correct answer + if (isSingleAnswer) return false; + // otherwise, don't change it + return currentValue; + }); const appCheckTokenResponse = await getToken(appCheck, false); const appCheckToken = appCheckTokenResponse.token; diff --git a/app-dev/party-game/package-lock.json b/app-dev/party-game/package-lock.json index a6bb2349..c54e3fad 100644 --- a/app-dev/party-game/package-lock.json +++ b/app-dev/party-game/package-lock.json @@ -10,16 +10,16 @@ "dependencies": { "@headlessui/react": "1.7.17", "@heroicons/react": "2.0.18", - "@types/node": "20.5.4", + "@types/node": "20.5.9", "@types/react": "18.2.21", "@types/react-dom": "18.2.7", "autoprefixer": "10.4.15", - "eslint": "8.47.0", + "eslint": "8.48.0", "eslint-config-next": "13.4.19", - "firebase": "10.3.0", + "firebase": "10.3.1", "firebase-admin": "11.10.1", "next": "13.4.19", - "postcss": "8.4.28", + "postcss": "8.4.29", "react": "18.2.0", "react-dom": "18.2.0", "react-qr-code": "2.0.12", @@ -117,9 +117,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", - "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -167,9 +167,9 @@ "license": "Apache-2.0" }, "node_modules/@firebase/app": { - "version": "0.9.17", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.9.17.tgz", - "integrity": "sha512-jBEGJ3km90M6sAc2O0baScDs0JAD1gGvZo7AniNVg06kvmaj+KEfGOaASwhlHNvIE2sYrOBqgobZADgaiYp5KQ==", + "version": "0.9.18", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.9.18.tgz", + "integrity": "sha512-SIJi3B/LzNezaEgbFCFIem12+51khkA3iewYljPQPUArWGSAr1cO9NK8TvtJWax5GMKSmQbJPqgi6a+gxHrWGQ==", "dependencies": { "@firebase/component": "0.6.4", "@firebase/logger": "0.4.0", @@ -215,11 +215,11 @@ "license": "Apache-2.0" }, "node_modules/@firebase/app-compat": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.17.tgz", - "integrity": "sha512-1vtnLdxe7lXN5jtv5hUpoKsHPvE56YUgelxmpY/OA1JELIH9ATBv/dsbDvSnsiaRkoQz/4F4B3iMma0JdxlIQA==", + "version": "0.2.18", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.18.tgz", + "integrity": "sha512-zUbAAZHhwmMUyaNFiFr+1Z/sfcxSQBFrRhpjzzpQMTfiV2C/+P0mC3BQA0HsysdGSYOlwrCs5rEGOyarhRU9Kw==", "dependencies": { - "@firebase/app": "0.9.17", + "@firebase/app": "0.9.18", "@firebase/component": "0.6.4", "@firebase/logger": "0.4.0", "@firebase/util": "1.9.3", @@ -321,9 +321,9 @@ } }, "node_modules/@firebase/firestore": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.1.2.tgz", - "integrity": "sha512-PZkXw2yfVcKgVvsd1rohOJ27bJz/p+TccwdraMgE701eDAj5xccGPmCVvMfFq0nz+BmQVuD5wUUVBBRMVCGOfw==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.1.3.tgz", + "integrity": "sha512-3kw/oZrLAIHuSDTAlKguZ1e0hAgWgiBl4QQm2mIPBvBAs++iEkuv9DH2tr6rbYpT6dWtdn6jj0RN0XeqOouJRg==", "dependencies": { "@firebase/component": "0.6.4", "@firebase/logger": "0.4.0", @@ -342,12 +342,12 @@ } }, "node_modules/@firebase/firestore-compat": { - "version": "0.3.16", - "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.16.tgz", - "integrity": "sha512-+UsOsLLtFLIU1fGjFZE6BL3LzEwknkrzv/U3ZcNk/C6cKcThuGBu53yZ6gxW9S7o1GUFiIey86HQi8kwdcAVZA==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.17.tgz", + "integrity": "sha512-Qh3tbE4vkn9XvyWnRaJM/v4vhCZ+btk2RZcq037o6oECHohaCFortevd/SKA4vA5yOx0/DwARIEv6XwgHkVucg==", "dependencies": { "@firebase/component": "0.6.4", - "@firebase/firestore": "4.1.2", + "@firebase/firestore": "4.1.3", "@firebase/firestore-types": "3.0.0", "@firebase/util": "1.9.3", "tslib": "^2.1.0" @@ -1159,9 +1159,9 @@ "optional": true }, "node_modules/@types/node": { - "version": "20.5.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.4.tgz", - "integrity": "sha512-Y9vbIAoM31djQZrPYjpTLo0XlaSwOIsrlfE3LpulZeRblttsLQRFRlBAppW0LOxyT3ALj2M5vU1ucQQayQH3jA==" + "version": "20.5.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.9.tgz", + "integrity": "sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ==" }, "node_modules/@types/prop-types": { "version": "15.7.5", @@ -2246,14 +2246,14 @@ } }, "node_modules/eslint": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", - "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "^8.47.0", + "@eslint/js": "8.48.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -2831,23 +2831,23 @@ } }, "node_modules/firebase": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/firebase/-/firebase-10.3.0.tgz", - "integrity": "sha512-1dy6KqAvGlnFUEFE+IaHQNygG/4v1OwB5KVIC/G1/qwbHEFws0FVHe3n/aRZ+Oo0tINO9s2Lim496p9wprMbNQ==", + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-10.3.1.tgz", + "integrity": "sha512-lUk1X0SQocShyIwz5x9mj829Yn1y4Y9KWriGLZ0/Pbwqt4ZxElx8rI1p/YAi4MZTtT1qi0wazo7dAlmuF6J0Aw==", "dependencies": { "@firebase/analytics": "0.10.0", "@firebase/analytics-compat": "0.2.6", - "@firebase/app": "0.9.17", + "@firebase/app": "0.9.18", "@firebase/app-check": "0.8.0", "@firebase/app-check-compat": "0.3.7", - "@firebase/app-compat": "0.2.17", + "@firebase/app-compat": "0.2.18", "@firebase/app-types": "0.9.0", "@firebase/auth": "1.3.0", "@firebase/auth-compat": "0.4.6", "@firebase/database": "1.0.1", "@firebase/database-compat": "1.0.1", - "@firebase/firestore": "4.1.2", - "@firebase/firestore-compat": "0.3.16", + "@firebase/firestore": "4.1.3", + "@firebase/firestore-compat": "0.3.17", "@firebase/functions": "0.10.0", "@firebase/functions-compat": "0.3.5", "@firebase/installations": "0.6.4", @@ -4678,9 +4678,9 @@ } }, "node_modules/postcss": { - "version": "8.4.28", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.28.tgz", - "integrity": "sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw==", + "version": "8.4.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", + "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", "funding": [ { "type": "opencollective", @@ -6031,9 +6031,9 @@ } }, "@eslint/js": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", - "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==" + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==" }, "@fastify/busboy": { "version": "1.2.1", @@ -6065,9 +6065,9 @@ "version": "0.8.0" }, "@firebase/app": { - "version": "0.9.17", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.9.17.tgz", - "integrity": "sha512-jBEGJ3km90M6sAc2O0baScDs0JAD1gGvZo7AniNVg06kvmaj+KEfGOaASwhlHNvIE2sYrOBqgobZADgaiYp5KQ==", + "version": "0.9.18", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.9.18.tgz", + "integrity": "sha512-SIJi3B/LzNezaEgbFCFIem12+51khkA3iewYljPQPUArWGSAr1cO9NK8TvtJWax5GMKSmQbJPqgi6a+gxHrWGQ==", "requires": { "@firebase/component": "0.6.4", "@firebase/logger": "0.4.0", @@ -6103,11 +6103,11 @@ "version": "0.5.0" }, "@firebase/app-compat": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.17.tgz", - "integrity": "sha512-1vtnLdxe7lXN5jtv5hUpoKsHPvE56YUgelxmpY/OA1JELIH9ATBv/dsbDvSnsiaRkoQz/4F4B3iMma0JdxlIQA==", + "version": "0.2.18", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.18.tgz", + "integrity": "sha512-zUbAAZHhwmMUyaNFiFr+1Z/sfcxSQBFrRhpjzzpQMTfiV2C/+P0mC3BQA0HsysdGSYOlwrCs5rEGOyarhRU9Kw==", "requires": { - "@firebase/app": "0.9.17", + "@firebase/app": "0.9.18", "@firebase/component": "0.6.4", "@firebase/logger": "0.4.0", "@firebase/util": "1.9.3", @@ -6188,9 +6188,9 @@ } }, "@firebase/firestore": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.1.2.tgz", - "integrity": "sha512-PZkXw2yfVcKgVvsd1rohOJ27bJz/p+TccwdraMgE701eDAj5xccGPmCVvMfFq0nz+BmQVuD5wUUVBBRMVCGOfw==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.1.3.tgz", + "integrity": "sha512-3kw/oZrLAIHuSDTAlKguZ1e0hAgWgiBl4QQm2mIPBvBAs++iEkuv9DH2tr6rbYpT6dWtdn6jj0RN0XeqOouJRg==", "requires": { "@firebase/component": "0.6.4", "@firebase/logger": "0.4.0", @@ -6203,12 +6203,12 @@ } }, "@firebase/firestore-compat": { - "version": "0.3.16", - "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.16.tgz", - "integrity": "sha512-+UsOsLLtFLIU1fGjFZE6BL3LzEwknkrzv/U3ZcNk/C6cKcThuGBu53yZ6gxW9S7o1GUFiIey86HQi8kwdcAVZA==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.17.tgz", + "integrity": "sha512-Qh3tbE4vkn9XvyWnRaJM/v4vhCZ+btk2RZcq037o6oECHohaCFortevd/SKA4vA5yOx0/DwARIEv6XwgHkVucg==", "requires": { "@firebase/component": "0.6.4", - "@firebase/firestore": "4.1.2", + "@firebase/firestore": "4.1.3", "@firebase/firestore-types": "3.0.0", "@firebase/util": "1.9.3", "tslib": "^2.1.0" @@ -6754,9 +6754,9 @@ "optional": true }, "@types/node": { - "version": "20.5.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.4.tgz", - "integrity": "sha512-Y9vbIAoM31djQZrPYjpTLo0XlaSwOIsrlfE3LpulZeRblttsLQRFRlBAppW0LOxyT3ALj2M5vU1ucQQayQH3jA==" + "version": "20.5.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.9.tgz", + "integrity": "sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ==" }, "@types/prop-types": { "version": "15.7.5" @@ -7395,14 +7395,14 @@ } }, "eslint": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", - "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "^8.47.0", + "@eslint/js": "8.48.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -7778,23 +7778,23 @@ } }, "firebase": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/firebase/-/firebase-10.3.0.tgz", - "integrity": "sha512-1dy6KqAvGlnFUEFE+IaHQNygG/4v1OwB5KVIC/G1/qwbHEFws0FVHe3n/aRZ+Oo0tINO9s2Lim496p9wprMbNQ==", + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-10.3.1.tgz", + "integrity": "sha512-lUk1X0SQocShyIwz5x9mj829Yn1y4Y9KWriGLZ0/Pbwqt4ZxElx8rI1p/YAi4MZTtT1qi0wazo7dAlmuF6J0Aw==", "requires": { "@firebase/analytics": "0.10.0", "@firebase/analytics-compat": "0.2.6", - "@firebase/app": "0.9.17", + "@firebase/app": "0.9.18", "@firebase/app-check": "0.8.0", "@firebase/app-check-compat": "0.3.7", - "@firebase/app-compat": "0.2.17", + "@firebase/app-compat": "0.2.18", "@firebase/app-types": "0.9.0", "@firebase/auth": "1.3.0", "@firebase/auth-compat": "0.4.6", "@firebase/database": "1.0.1", "@firebase/database-compat": "1.0.1", - "@firebase/firestore": "4.1.2", - "@firebase/firestore-compat": "0.3.16", + "@firebase/firestore": "4.1.3", + "@firebase/firestore-compat": "0.3.17", "@firebase/functions": "0.10.0", "@firebase/functions-compat": "0.3.5", "@firebase/installations": "0.6.4", @@ -8883,9 +8883,9 @@ "version": "4.0.6" }, "postcss": { - "version": "8.4.28", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.28.tgz", - "integrity": "sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw==", + "version": "8.4.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", + "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", "requires": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", diff --git a/app-dev/party-game/package.json b/app-dev/party-game/package.json index f2d3c8ac..7924121e 100644 --- a/app-dev/party-game/package.json +++ b/app-dev/party-game/package.json @@ -6,21 +6,22 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "update": "npx npm-check-updates -u && npm install" }, "dependencies": { "@headlessui/react": "1.7.17", "@heroicons/react": "2.0.18", - "@types/node": "20.5.4", + "@types/node": "20.5.9", "@types/react": "18.2.21", "@types/react-dom": "18.2.7", "autoprefixer": "10.4.15", - "eslint": "8.47.0", + "eslint": "8.48.0", "eslint-config-next": "13.4.19", - "firebase": "10.3.0", + "firebase": "10.3.1", "firebase-admin": "11.10.1", "next": "13.4.19", - "postcss": "8.4.28", + "postcss": "8.4.29", "react": "18.2.0", "react-dom": "18.2.0", "react-qr-code": "2.0.12", From 9431a528643d19e3271b88146bb49358695e5175 Mon Sep 17 00:00:00 2001 From: Luke Schlangen Date: Wed, 6 Sep 2023 10:50:10 -0500 Subject: [PATCH 3/3] feat: add app check and auth tokens utility --- .../app/components/border-countdown-timer.tsx | 8 ++---- .../app/components/create-game-form.tsx | 11 +++----- .../app/components/delete-game-button.tsx | 11 ++------ .../app/components/exit-game-button.tsx | 10 ++------ .../app/components/question-panel.tsx | 9 ++----- .../app/components/start-game-button.tsx | 10 ++------ app-dev/party-game/app/hooks/use-game.ts | 9 +++---- .../party-game/app/lib/request-formatter.ts | 25 +++++++++++++++++++ 8 files changed, 41 insertions(+), 52 deletions(-) create mode 100644 app-dev/party-game/app/lib/request-formatter.ts diff --git a/app-dev/party-game/app/components/border-countdown-timer.tsx b/app-dev/party-game/app/components/border-countdown-timer.tsx index 587e794d..bb227722 100644 --- a/app-dev/party-game/app/components/border-countdown-timer.tsx +++ b/app-dev/party-game/app/components/border-countdown-timer.tsx @@ -21,8 +21,7 @@ import {DocumentReference, Timestamp} from 'firebase/firestore'; import {useEffect, useState} from 'react'; import useFirebaseAuthentication from '@/app/hooks/use-firebase-authentication'; import {nudgeGameAction} from '@/app/actions/nudge-game'; -import {appCheck} from '@/app/lib/firebase-client-initialization'; -import {getToken} from 'firebase/app-check'; +import {addTokens} from '@/app/lib/request-formatter'; export default function BorderCountdownTimer({game, children, gameRef}: { game: Game, children: React.ReactNode, gameRef: DocumentReference }) { const [timeLeft, setTimeLeft] = useState(game.timePerQuestion); @@ -34,10 +33,7 @@ export default function BorderCountdownTimer({game, children, gameRef}: { game: const timeToCountDown = isShowingCorrectAnswers ? game.timePerAnswer : game.timePerQuestion; const nudgeGame = async ({gameId}: {gameId: string}) => { - const appCheckTokenResponse = await getToken(appCheck, false); - const appCheckToken = appCheckTokenResponse.token; - const token = await authUser.getIdToken(); - nudgeGameAction({gameId, token, appCheckToken}); + nudgeGameAction(await addTokens({gameId})); }; useEffect(() => { diff --git a/app-dev/party-game/app/components/create-game-form.tsx b/app-dev/party-game/app/components/create-game-form.tsx index 8f1ef694..8dfaf64d 100644 --- a/app-dev/party-game/app/components/create-game-form.tsx +++ b/app-dev/party-game/app/components/create-game-form.tsx @@ -16,18 +16,15 @@ 'use client'; -import useFirebaseAuthentication from '@/app/hooks/use-firebase-authentication'; import {useRouter} from 'next/navigation'; import {useEffect, useState} from 'react'; import BigColorBorderButton from './big-color-border-button'; import {TimePerAnswerSchema, TimePerQuestionSchema} from '@/app/types'; import {createGameAction} from '@/app/actions/create-game'; import {z} from 'zod'; -import {getToken} from 'firebase/app-check'; -import {appCheck} from '@/app/lib/firebase-client-initialization'; +import {addTokens} from '@/app/lib/request-formatter'; export default function CreateGameForm() { - const authUser = useFirebaseAuthentication(); const defaultTimePerQuestion = 60; const defaultTimePerAnswer = 20; const [timePerQuestionInputValue, setTimePerQuestionInputValue] = useState(defaultTimePerQuestion.toString()); @@ -40,11 +37,9 @@ export default function CreateGameForm() { const router = useRouter(); const onCreateGameSubmit = async (event: React.FormEvent) => { event.preventDefault(); - const appCheckTokenResponse = await getToken(appCheck, false); - const appCheckToken = appCheckTokenResponse.token; - const token = await authUser.getIdToken(); try { - const response = await createGameAction({gameSettings: {timePerQuestion, timePerAnswer}, token, appCheckToken}); + const gameSettings = {timePerQuestion, timePerAnswer}; + const response = await createGameAction(await addTokens({gameSettings})); router.push(`/game/${response.gameId}`); } catch (error) { setSubmissionErrorMessage('There was an error handling the request.'); diff --git a/app-dev/party-game/app/components/delete-game-button.tsx b/app-dev/party-game/app/components/delete-game-button.tsx index 31a84202..e9f96a0e 100644 --- a/app-dev/party-game/app/components/delete-game-button.tsx +++ b/app-dev/party-game/app/components/delete-game-button.tsx @@ -16,19 +16,12 @@ 'use client'; -import useFirebaseAuthentication from '@/app/hooks/use-firebase-authentication'; import {deleteGameAction} from '@/app/actions/delete-game'; -import {appCheck} from '@/app/lib/firebase-client-initialization'; -import {getToken} from 'firebase/app-check'; +import {addTokens} from '../lib/request-formatter'; export default function DeleteGameButton({gameId}: { gameId: string }) { - const authUser = useFirebaseAuthentication(); - const onDeleteGameClick = async () => { - const appCheckTokenResponse = await getToken(appCheck, false); - const appCheckToken = appCheckTokenResponse.token; - const token = await authUser.getIdToken(); - await deleteGameAction({gameId, token, appCheckToken}); + await deleteGameAction(await addTokens({gameId})); }; return ( diff --git a/app-dev/party-game/app/components/exit-game-button.tsx b/app-dev/party-game/app/components/exit-game-button.tsx index c9230d29..ad6697e3 100644 --- a/app-dev/party-game/app/components/exit-game-button.tsx +++ b/app-dev/party-game/app/components/exit-game-button.tsx @@ -16,21 +16,15 @@ 'use client'; -import useFirebaseAuthentication from '@/app/hooks/use-firebase-authentication'; import {useRouter} from 'next/navigation'; import {exitGameAction} from '@/app/actions/exit-game'; -import {getToken} from 'firebase/app-check'; -import {appCheck} from '@/app/lib/firebase-client-initialization'; +import {addTokens} from '../lib/request-formatter'; export default function ExitGameButton({gameId}: { gameId: string }) { - const authUser = useFirebaseAuthentication(); const router = useRouter(); const onExitGameClick = async () => { - const appCheckTokenResponse = await getToken(appCheck, false); - const appCheckToken = appCheckTokenResponse.token; - const token = await authUser.getIdToken(); - await exitGameAction({gameId, token, appCheckToken}); + await exitGameAction(await addTokens({gameId})); router.push('/'); }; diff --git a/app-dev/party-game/app/components/question-panel.tsx b/app-dev/party-game/app/components/question-panel.tsx index f0d8fada..15dd6efa 100644 --- a/app-dev/party-game/app/components/question-panel.tsx +++ b/app-dev/party-game/app/components/question-panel.tsx @@ -25,8 +25,7 @@ import {useEffect, useState} from 'react'; import Scoreboard from './scoreboard'; import useScoreboard from '@/app/hooks/use-scoreboard'; import {updateAnswerAction} from '@/app/actions/update-answer'; -import {appCheck} from '@/app/lib/firebase-client-initialization'; -import {getToken} from 'firebase/app-check'; +import {addTokens} from '../lib/request-formatter'; export default function QuestionPanel({game, gameRef, currentQuestion}: { game: Game, gameRef: DocumentReference, currentQuestion: Question }) { const authUser = useFirebaseAuthentication(); @@ -63,11 +62,7 @@ export default function QuestionPanel({game, gameRef, currentQuestion}: { game: // otherwise, don't change it return currentValue; }); - - const appCheckTokenResponse = await getToken(appCheck, false); - const appCheckToken = appCheckTokenResponse.token; - const token = await authUser.getIdToken(); - await updateAnswerAction({gameId, answerSelection: newAnswerSelection, token, appCheckToken}); + await updateAnswerAction(await addTokens({gameId, answerSelection: newAnswerSelection})); } }; diff --git a/app-dev/party-game/app/components/start-game-button.tsx b/app-dev/party-game/app/components/start-game-button.tsx index 3414d3a9..5172031f 100644 --- a/app-dev/party-game/app/components/start-game-button.tsx +++ b/app-dev/party-game/app/components/start-game-button.tsx @@ -16,20 +16,14 @@ 'use client'; -import useFirebaseAuthentication from '@/app/hooks/use-firebase-authentication'; import './big-color-border-button.css'; import BigColorBorderButton from '@/app/components/big-color-border-button'; import {startGameAction} from '@/app/actions/start-game'; -import {getToken} from 'firebase/app-check'; -import {appCheck} from '@/app/lib/firebase-client-initialization'; +import {addTokens} from '@/app/lib/request-formatter'; export default function StartGameButton({gameId}: {gameId: string}) { - const authUser = useFirebaseAuthentication(); const onStartGameClick = async () => { - const appCheckTokenResponse = await getToken(appCheck, false); - const appCheckToken = appCheckTokenResponse.token; - const token = await authUser.getIdToken(); - await startGameAction({gameId, token, appCheckToken}); + await startGameAction(await addTokens({gameId})); }; return ( diff --git a/app-dev/party-game/app/hooks/use-game.ts b/app-dev/party-game/app/hooks/use-game.ts index 2ab2d636..ac234406 100644 --- a/app-dev/party-game/app/hooks/use-game.ts +++ b/app-dev/party-game/app/hooks/use-game.ts @@ -15,13 +15,13 @@ */ import {useEffect, useState} from 'react'; -import {appCheck, db} from '@/app/lib/firebase-client-initialization'; +import {db} from '@/app/lib/firebase-client-initialization'; import {Game, GameSchema, emptyGame, gameStates} from '@/app/types'; import {doc, onSnapshot} from 'firebase/firestore'; import {usePathname} from 'next/navigation'; import useFirebaseAuthentication from './use-firebase-authentication'; import {joinGameAction} from '@/app/actions/join-game'; -import {getToken} from 'firebase/app-check'; +import {addTokens} from '../lib/request-formatter'; const useGame = () => { const pathname = usePathname(); @@ -32,10 +32,7 @@ const useGame = () => { useEffect(() => { const joinGame = async () => { - const appCheckTokenResponse = await getToken(appCheck, false); - const appCheckToken = appCheckTokenResponse.token; - const token = await authUser.getIdToken(); - joinGameAction({gameId, token, appCheckToken}); + joinGameAction(await addTokens({gameId})); }; if (game.leader.uid && authUser.uid && game.leader.uid !== authUser.uid) { joinGame(); diff --git a/app-dev/party-game/app/lib/request-formatter.ts b/app-dev/party-game/app/lib/request-formatter.ts new file mode 100644 index 00000000..b5378816 --- /dev/null +++ b/app-dev/party-game/app/lib/request-formatter.ts @@ -0,0 +1,25 @@ +/** + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {appCheck, auth} from '@/app/lib/firebase-client-initialization'; +import {getToken} from 'firebase/app-check'; + +export async function addTokens(requestBody: any) { + const appCheckTokenResponse = await getToken(appCheck, false); + const appCheckToken = appCheckTokenResponse.token; + const token = await auth.currentUser?.getIdToken(); + return {...requestBody, token, appCheckToken}; +}