diff options
| author | nsfisis <nsfisis@gmail.com> | 2024-07-29 20:04:03 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2024-07-29 20:04:09 +0900 |
| commit | 648613e24c8afe5fd3c599def61b33ccf7bcb96c (patch) | |
| tree | f368aa1ef1d734d3096c9129e17d6af11d1041a6 /frontend | |
| parent | d73fd8bf5bf589a4a391c867e980761fadb647ce (diff) | |
| download | iosdc-japan-2024-albatross-648613e24c8afe5fd3c599def61b33ccf7bcb96c.tar.gz iosdc-japan-2024-albatross-648613e24c8afe5fd3c599def61b33ccf7bcb96c.tar.zst iosdc-japan-2024-albatross-648613e24c8afe5fd3c599def61b33ccf7bcb96c.zip | |
feat: authenticate WebSocket connection by short-lived access token
Diffstat (limited to 'frontend')
| -rw-r--r-- | frontend/app/.server/api/schema.d.ts | 53 | ||||
| -rw-r--r-- | frontend/app/components/GolfPlayApp.tsx | 14 | ||||
| -rw-r--r-- | frontend/app/components/GolfWatchApp.tsx | 14 | ||||
| -rw-r--r-- | frontend/app/routes/golf.$gameId.play.tsx | 49 | ||||
| -rw-r--r-- | frontend/app/routes/golf.$gameId.watch.tsx | 49 |
5 files changed, 143 insertions, 36 deletions
diff --git a/frontend/app/.server/api/schema.d.ts b/frontend/app/.server/api/schema.d.ts index 1d3313e..cd409f7 100644 --- a/frontend/app/.server/api/schema.d.ts +++ b/frontend/app/.server/api/schema.d.ts @@ -64,6 +64,59 @@ export interface paths { patch?: never; trace?: never; }; + "/token": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get a short-lived access token */ + get: { + parameters: { + query?: never; + header: { + Authorization: string; + }; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successfully authenticated */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example xxxxx.xxxxx.xxxxx */ + token: string; + }; + }; + }; + /** @description Forbidden */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Forbidden operation */ + message: string; + }; + }; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/games": { parameters: { query?: never; diff --git a/frontend/app/components/GolfPlayApp.tsx b/frontend/app/components/GolfPlayApp.tsx index 13afb22..3cb512a 100644 --- a/frontend/app/components/GolfPlayApp.tsx +++ b/frontend/app/components/GolfPlayApp.tsx @@ -15,12 +15,18 @@ type Problem = components["schemas"]["Problem"]; type GameState = "connecting" | "waiting" | "starting" | "gaming" | "finished"; -export default function GolfPlayApp({ game }: { game: Game }) { - // const socketUrl = `wss://t.nil.ninja/iosdc/2024/sock/golf/${game.game_id}/play`; +export default function GolfPlayApp({ + game, + sockToken, +}: { + game: Game; + sockToken: string; +}) { + // const socketUrl = `wss://t.nil.ninja/iosdc/2024/sock/golf/${game.game_id}/play?token=${sockToken}`; const socketUrl = process.env.NODE_ENV === "development" - ? `ws://localhost:8002/sock/golf/${game.game_id}/play` - : `ws://api-server/sock/golf/${game.game_id}/play`; + ? `ws://localhost:8002/sock/golf/${game.game_id}/play?token=${sockToken}` + : `ws://api-server/sock/golf/${game.game_id}/play?token=${sockToken}`; const { sendJsonMessage, lastJsonMessage, readyState } = useWebSocket<WebSocketMessage>(socketUrl, {}); diff --git a/frontend/app/components/GolfWatchApp.tsx b/frontend/app/components/GolfWatchApp.tsx index bcd1f0f..00ad005 100644 --- a/frontend/app/components/GolfWatchApp.tsx +++ b/frontend/app/components/GolfWatchApp.tsx @@ -14,12 +14,18 @@ type Problem = components["schemas"]["Problem"]; type GameState = "connecting" | "waiting" | "starting" | "gaming" | "finished"; -export default function GolfWatchApp({ game }: { game: Game }) { - // const socketUrl = `wss://t.nil.ninja/iosdc/2024/sock/golf/${game.game_id}/play`; +export default function GolfWatchApp({ + game, + sockToken, +}: { + game: Game; + sockToken: string; +}) { + // const socketUrl = `wss://t.nil.ninja/iosdc/2024/sock/golf/${game.game_id}/watch?token=${sockToken}`; const socketUrl = process.env.NODE_ENV === "development" - ? `ws://localhost:8002/sock/golf/${game.game_id}/play` - : `ws://api-server/sock/golf/${game.game_id}/play`; + ? `ws://localhost:8002/sock/golf/${game.game_id}/watch?token=${sockToken}` + : `ws://api-server/sock/golf/${game.game_id}/watch?token=${sockToken}`; const { lastJsonMessage, readyState } = useWebSocket<WebSocketMessage>( socketUrl, diff --git a/frontend/app/routes/golf.$gameId.play.tsx b/frontend/app/routes/golf.$gameId.play.tsx index bda563f..78ce585 100644 --- a/frontend/app/routes/golf.$gameId.play.tsx +++ b/frontend/app/routes/golf.$gameId.play.tsx @@ -8,26 +8,47 @@ export async function loader({ params, request }: LoaderFunctionArgs) { const { token } = await isAuthenticated(request, { failureRedirect: "/login", }); - const { data, error } = await apiClient.GET("/games/{game_id}", { - params: { - path: { - game_id: Number(params.gameId), + + const fetchGame = async () => { + const { data, error } = await apiClient.GET("/games/{game_id}", { + params: { + path: { + game_id: Number(params.gameId), + }, + header: { + Authorization: `Bearer ${token}`, + }, }, - header: { - Authorization: `Bearer ${token}`, + }); + if (error) { + throw new Error(error.message); + } + return data; + }; + + const fetchSockToken = async () => { + const { data, error } = await apiClient.GET("/token", { + params: { + header: { + Authorization: `Bearer ${token}`, + }, }, - }, - }); - if (error) { - throw new Error(error.message); - } + }); + if (error) { + throw new Error(error.message); + } + return data.token; + }; + + const [game, sockToken] = await Promise.all([fetchGame(), fetchSockToken()]); return { - game: data, + game, + sockToken, }; } export default function GolfPlay() { - const { game } = useLoaderData<typeof loader>(); + const { game, sockToken } = useLoaderData<typeof loader>(); - return <GolfPlayApp game={game} />; + return <GolfPlayApp game={game} sockToken={sockToken} />; } diff --git a/frontend/app/routes/golf.$gameId.watch.tsx b/frontend/app/routes/golf.$gameId.watch.tsx index e1cb5d7..28c17cc 100644 --- a/frontend/app/routes/golf.$gameId.watch.tsx +++ b/frontend/app/routes/golf.$gameId.watch.tsx @@ -8,26 +8,47 @@ export async function loader({ params, request }: LoaderFunctionArgs) { const { token } = await isAuthenticated(request, { failureRedirect: "/login", }); - const { data, error } = await apiClient.GET("/games/{game_id}", { - params: { - path: { - game_id: Number(params.gameId), + + const fetchGame = async () => { + const { data, error } = await apiClient.GET("/games/{game_id}", { + params: { + path: { + game_id: Number(params.gameId), + }, + header: { + Authorization: `Bearer ${token}`, + }, }, - header: { - Authorization: `Bearer ${token}`, + }); + if (error) { + throw new Error(error.message); + } + return data; + }; + + const fetchSockToken = async () => { + const { data, error } = await apiClient.GET("/token", { + params: { + header: { + Authorization: `Bearer ${token}`, + }, }, - }, - }); - if (error) { - throw new Error(error.message); - } + }); + if (error) { + throw new Error(error.message); + } + return data.token; + }; + + const [game, sockToken] = await Promise.all([fetchGame(), fetchSockToken()]); return { - game: data, + game, + sockToken, }; } export default function GolfWatch() { - const { game } = useLoaderData<typeof loader>(); + const { game, sockToken } = useLoaderData<typeof loader>(); - return <GolfWatchApp game={game} />; + return <GolfWatchApp game={game} sockToken={sockToken} />; } |
