From 4da9e20f0504f666a3b49d0a351035562e916287 Mon Sep 17 00:00:00 2001 From: Grant Forrest Date: Fri, 14 Jun 2024 13:32:16 -0400 Subject: [PATCH] use auth-client fetch --- apps/gnocchi/verdant/package.json | 2 +- packages/client/package.json | 3 +- packages/client/src/fetch.ts | 105 +++++------------------------- pnpm-lock.yaml | 83 ++++++++++++----------- server/package.json | 2 +- server/src/server.ts | 2 +- web/package.json | 4 +- 7 files changed, 69 insertions(+), 132 deletions(-) diff --git a/apps/gnocchi/verdant/package.json b/apps/gnocchi/verdant/package.json index e1f33da3..d740f2bc 100644 --- a/apps/gnocchi/verdant/package.json +++ b/apps/gnocchi/verdant/package.json @@ -22,7 +22,7 @@ "react": "18.3.1" }, "dependencies": { - "@0no-co/graphqlsp": "^1.3.4", + "@0no-co/graphqlsp": "1.12.8", "@biscuits/client": "workspace:*", "@verdant-web/cli": "^4.4.0", "@verdant-web/common": "2.3.1", diff --git a/packages/client/package.json b/packages/client/package.json index ffe77b07..08176870 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -38,7 +38,8 @@ }, "type": "module", "dependencies": { - "@0no-co/graphqlsp": "^1.3.4", + "@0no-co/graphqlsp": "1.12.8", + "@a-type/auth-client": "1.0.6", "@apollo/client": "^3.9.9", "@biscuits/apps": "workspace:*", "@biscuits/error": "workspace:*", diff --git a/packages/client/src/fetch.ts b/packages/client/src/fetch.ts index 27ee4cdd..4e871637 100644 --- a/packages/client/src/fetch.ts +++ b/packages/client/src/fetch.ts @@ -1,98 +1,25 @@ import { BiscuitsError } from '@biscuits/error'; -import { CONFIG } from './index.js'; - -// wrap fetch to automatically redirect to /join on 401 -export const fetch: typeof window.fetch = async (input: any, init: any) => { - // ensure cookies are always sent - if (typeof input === 'object') { - input.credentials = 'include'; - } - if (typeof init === 'object') { - init.credentials = 'include'; - } - - const requestUrlString = typeof input === 'string' ? input : input.url; - const requestUrlOrigin = new URL(requestUrlString).origin; - - const response = await window.fetch.bind(window)(input, init); - - const { body, clone } = await peekAtResponseBody(response); - const biscuitsError = BiscuitsError.readResponseBody(body); - if (biscuitsError) { - console.error('Biscuits Error', biscuitsError); - if (biscuitsError.code === BiscuitsError.Code.SessionExpired) { - // if the session expired, we need to refresh it - const refreshSuccess = await refreshSession(requestUrlOrigin); - if (refreshSuccess) { - // retry the original request - return fetch(input, init); - } else { - // failed to refresh the session - the user needs - // to log in again - } +import { API_ORIGIN, HOME_ORIGIN } from './config.js'; +import { createFetch } from '@a-type/auth-client'; + +export { refreshSession } from '@a-type/auth-client'; + +export const fetch = createFetch({ + readBody: true, + refreshSessionEndpoint: `${API_ORIGIN}/auth/refresh`, + isSessionExpired: (res, body) => { + const biscuitsError = BiscuitsError.readResponseBody(body); + if (biscuitsError) { + console.error('Biscuits Error', biscuitsError); + return biscuitsError.code === BiscuitsError.Code.SessionExpired; } - } - return clone; -}; - -export async function refreshSession(apiOrigin: string) { - if (!refreshPromise) { - refreshPromise = refreshSessionInternal(apiOrigin); - refreshPromise.finally(() => { - refreshPromise = null; - }); - } - return refreshPromise; -} - -let refreshPromise: Promise | null = null; -async function refreshSessionInternal(apiOrigin: string) { - try { - const response = await fetch(`${apiOrigin}/auth/refresh`, { - method: 'POST', - }); - if (response.ok) { - const body = await response.json(); - if (body.ok) { - console.info('session refreshed'); - } else { - console.error('session refresh failed', body); - } - } else if (response.status === 401 || response.status === 403) { - console.error('session refresh failed', response.status); - } else { - console.error('session refresh failed', response.status); - } - return response.ok; - } catch (e) { - console.error(e); return false; - } -} - -async function peekAtResponseBody(response: Response): Promise<{ - body: any; - clone: Response; -}> { - const clone = response.clone(); - try { - const body = await response.json(); - return { - body, - clone, - }; - } catch (e) { - console.error(e); - } - return { - body: null, - clone, - }; -} + }, +}); export function login() { window.location.href = - CONFIG.HOME_ORIGIN + + HOME_ORIGIN + '/login' + '?returnTo=' + encodeURIComponent(window.location.href); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4ce2fc30..44016b95 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -147,8 +147,8 @@ importers: apps/gnocchi/verdant: dependencies: '@0no-co/graphqlsp': - specifier: ^1.3.4 - version: 1.3.4 + specifier: 1.12.8 + version: 1.12.8(graphql@16.8.1)(typescript@5.4.5) '@biscuits/client': specifier: workspace:* version: link:../../../packages/client @@ -990,8 +990,11 @@ importers: packages/client: dependencies: '@0no-co/graphqlsp': - specifier: ^1.3.4 - version: 1.3.4 + specifier: 1.12.8 + version: 1.12.8(graphql@16.8.1)(typescript@5.4.5) + '@a-type/auth-client': + specifier: 1.0.6 + version: 1.0.6(@a-type/ui@0.8.20)(react@18.3.1) '@apollo/client': specifier: ^3.9.9 version: 3.9.9(@types/react@18.3.3)(graphql@16.8.1)(react-dom@18.3.1)(react@18.3.1) @@ -1080,8 +1083,8 @@ importers: server: dependencies: '@a-type/auth': - specifier: 0.6.5 - version: 0.6.5 + specifier: 0.6.6 + version: 0.6.6 '@a-type/utils': specifier: 1.1.3 version: 1.1.3 @@ -1225,8 +1228,8 @@ importers: web: dependencies: '@a-type/auth-client': - specifier: 1.0.4 - version: 1.0.4(@a-type/ui@0.8.20)(react@18.3.1) + specifier: 1.0.6 + version: 1.0.6(@a-type/ui@0.8.20)(react@18.3.1) '@a-type/ui': specifier: ^0.8.20 version: 0.8.20(@types/react-dom@18.3.0)(@types/react@18.3.3)(csstype@3.1.3)(react-dom@18.3.1)(react@18.3.1) @@ -1292,8 +1295,8 @@ importers: version: 0.165.0 devDependencies: '@0no-co/graphqlsp': - specifier: ^1.3.4 - version: 1.3.4 + specifier: 1.12.8 + version: 1.12.8(graphql@16.8.1)(typescript@5.4.5) '@originjs/vite-plugin-commonjs': specifier: ^1.0.3 version: 1.0.3 @@ -1350,25 +1353,38 @@ packages: graphql: 16.8.1 dev: false - /@0no-co/graphqlsp@1.3.4: - resolution: {integrity: sha512-xIVgzECqzAjReuKzW9d7NVDb1HBfrkgZ1AxdqN/WfgPpVfbWKrXpDNJrbhNhtdRB0+wd6iF4FaEtFJA2QvnPDw==} + /@0no-co/graphql.web@1.0.7(graphql@16.8.1): + resolution: {integrity: sha512-E3Qku4mTzdrlwVWGPxklDnME5ANrEGetvYw4i2GCRlppWXXE4QD66j7pwb8HelZwS6LnqEChhrSOGCXpbiu6MQ==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 + peerDependenciesMeta: + graphql: + optional: true dependencies: - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding + graphql: 16.8.1 + + /@0no-co/graphqlsp@1.12.8(graphql@16.8.1)(typescript@5.4.5): + resolution: {integrity: sha512-arW3ZzifyKIJhehoAlsP069AX73EN6q358bOCf+8z00PmfPR2DSxq7uGGSj0oFNe3vEDtGYl88HmFEWQ+0+dwQ==} + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 + typescript: ^5.0.0 + dependencies: + '@gql.tada/internal': 1.0.1(graphql@16.8.1)(typescript@5.4.5) + graphql: 16.8.1 + typescript: 5.4.5 - /@a-type/auth-client@1.0.4(@a-type/ui@0.8.20)(react@18.3.1): - resolution: {integrity: sha512-SU8hGqNuusvVwbtQ/0shugYhLN6hbpTUmQbLQntj5Bmg9ST5tomzhLzz4ek/+/bdWmnOr4c2Hx5g2eDJl4u0NQ==} + /@a-type/auth-client@1.0.6(@a-type/ui@0.8.20)(react@18.3.1): + resolution: {integrity: sha512-AGKAEpExelRupmveEVfYzpwEMVwAPJ6fCqdpJKQeL41rHyCY/CpwAF4+XG7AEFgU/1EzEjr00MYEC1QQNJZzzw==} peerDependencies: '@a-type/ui': ^0.8.0 react: ^18 dependencies: - '@a-type/ui': 0.8.20(@types/react-dom@18.3.0)(@types/react@18.3.3)(csstype@3.1.3)(react-dom@18.3.1)(react@18.3.1) + '@a-type/ui': 0.8.20(@types/react@18.3.3)(csstype@3.1.3)(react-dom@18.3.1)(react@18.3.1) react: 18.3.1 dev: false - /@a-type/auth@0.6.5: - resolution: {integrity: sha512-qYqClgdPHjCCkTB8WF3ukF/i3nfRyhLmMAOi39cmfBaPwJxn3H7PAixq6RYWMr4qFZY8oXE9d4EU5TCfQfhnfQ==} + /@a-type/auth@0.6.6: + resolution: {integrity: sha512-HpWfgksPpcEqgrUwVrL3iwJMzrc5i7bMrgvUqZclQxPP31fYhq4AoxFnVMzp24IMUyrXoNwGS7W2GqVkYoIPSA==} dependencies: cookie: 0.6.0 discord-oauth2: 2.12.1 @@ -1651,7 +1667,6 @@ packages: - '@types/react' - '@types/react-dom' - csstype - dev: true /@a-type/ui@0.8.20(csstype@3.1.3)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-XFwp9ghCqtcLCSyVe7X7ZCMSKqGPBryCJxRUZDS+LSo4gODTSTJjjcMW0k4ZqeO9A/JvvZACO2Sc4FtX1LwPDw==} @@ -6003,6 +6018,16 @@ packages: crypto-js: 4.2.0 dev: false + /@gql.tada/internal@1.0.1(graphql@16.8.1)(typescript@5.4.5): + resolution: {integrity: sha512-VglE9pEUqCD7YwytLehI1RiEpxJsEkO+8onrMI8DEFTcrn+Irv2tQfPUHBC0LjtY5sFgoodCSLIcnxyZOKggYA==} + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 + typescript: ^5.0.0 + dependencies: + '@0no-co/graphql.web': 1.0.7(graphql@16.8.1) + graphql: 16.8.1 + typescript: 5.4.5 + /@graphql-tools/executor@1.2.0(graphql@16.8.1): resolution: {integrity: sha512-SKlIcMA71Dha5JnEWlw4XxcaJ+YupuXg0QCZgl2TOLFz4SkGCwU/geAsJvUJFwK2RbVLpQv/UMq67lOaBuwDtg==} engines: {node: '>=16.0.0'} @@ -6808,7 +6833,6 @@ packages: '@types/react': 18.3.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - dev: true /@radix-ui/react-accordion@1.1.2(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-fDG7jcoNKVjSK6yfmuAs0EnPDro0WMXIhMtXdTBWqEioVW206ku+4Lw07e+13lUkFkpoEQ2PdeMIAGpdqEAmDg==} @@ -7357,7 +7381,6 @@ packages: '@types/react': 18.3.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - dev: true /@radix-ui/react-context-menu@2.1.5(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-R5XaDj06Xul1KGb+WP8qiOh7tKJNz2durpLBXAGZjSVtctcRFCuEvy2gtMwRJGePwQQE5nV77gs4FwRi8T+r2g==} @@ -7543,7 +7566,6 @@ packages: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-remove-scroll: 2.5.5(@types/react@18.3.3)(react@18.3.1) - dev: true /@radix-ui/react-dialog@1.0.5(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==} @@ -7806,7 +7828,6 @@ packages: '@types/react': 18.3.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - dev: true /@radix-ui/react-dropdown-menu@2.0.6(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==} @@ -8134,7 +8155,6 @@ packages: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-remove-scroll: 2.5.5(@types/react@18.3.3)(react@18.3.1) - dev: true /@radix-ui/react-menu@2.0.6(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==} @@ -8309,7 +8329,6 @@ packages: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-remove-scroll: 2.5.5(@types/react@18.3.3)(react@18.3.1) - dev: true /@radix-ui/react-popover@1.0.7(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==} @@ -8461,7 +8480,6 @@ packages: '@types/react': 18.3.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - dev: true /@radix-ui/react-popper@1.1.3(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==} @@ -8877,7 +8895,6 @@ packages: '@types/react': 18.3.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - dev: true /@radix-ui/react-radio-group@1.1.3(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-x+yELayyefNeKeTx4fjK6j99Fs6c4qKm3aY38G3swQVTN6xMpsrbigC0uHs2L//g8q4qR7qOcww8430jJmi2ag==} @@ -9020,7 +9037,6 @@ packages: '@types/react': 18.3.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - dev: true /@radix-ui/react-roving-focus@1.0.4(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==} @@ -9210,7 +9226,6 @@ packages: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-remove-scroll: 2.5.5(@types/react@18.3.3)(react@18.3.1) - dev: true /@radix-ui/react-select@2.0.0(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-RH5b7af4oHtkcHS7pG6Sgv5rk5Wxa7XI8W5gvB1N/yiuDGZxko1ynvOiVhFM7Cis2A8zxF9bTOUVbRDzPepe6w==} @@ -9372,7 +9387,6 @@ packages: '@types/react': 18.3.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - dev: true /@radix-ui/react-slider@1.1.2(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-NKs15MJylfzVsCagVSWKhGGLNR1W9qWs+HtgbmjjVUB3B9+lb3PYoXxVju3kOrpf0VKyVCtZp+iTwVoqpa1Chw==} @@ -9646,7 +9660,6 @@ packages: '@types/react': 18.3.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - dev: true /@radix-ui/react-tabs@1.0.4(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-egZfYY/+wRNCflXNHx+dePvnz9FbmssDTJBtgRfDY7e8SE5oIo3Py2eCB1ckAbh1Q7cQ/6yJZThJ++sgbxibog==} @@ -9799,7 +9812,6 @@ packages: '@types/react': 18.3.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - dev: true /@radix-ui/react-toast@1.1.5(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-fRLn227WHIBRSzuRzGJ8W+5YALxofH23y0MlPLddaIpLpCDqdE0NZlS2NRQDRiptfxDeeCjgFIpexB1/zkxDlw==} @@ -9936,7 +9948,6 @@ packages: '@types/react': 18.3.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - dev: true /@radix-ui/react-toggle-group@1.0.4(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-Uaj/M/cMyiyT9Bx6fOZO0SAG4Cls0GptBWiBmBxofmDbNVnYYoyRWj/2M/6VCi/7qcXFWnHhRUfdfZFvvkuu8A==} @@ -10177,7 +10188,6 @@ packages: '@types/react': 18.3.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - dev: true /@radix-ui/react-tooltip@1.0.7(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==} @@ -17644,7 +17654,6 @@ packages: /graphql@16.8.1: resolution: {integrity: sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} - dev: false /gray-matter@4.0.3: resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} diff --git a/server/package.json b/server/package.json index 076c723e..3e22dfe5 100644 --- a/server/package.json +++ b/server/package.json @@ -19,7 +19,7 @@ "type": "module", "private": true, "dependencies": { - "@a-type/auth": "0.6.5", + "@a-type/auth": "0.6.6", "@a-type/utils": "1.1.3", "@aws-sdk/client-cloudfront": "^3.583.0", "@aws-sdk/client-s3": "3.583.0", diff --git a/server/src/server.ts b/server/src/server.ts index 1e4356af..274e7888 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -4,7 +4,7 @@ import { productAdminSetup } from './tasks/productAdminSetup.js'; import { ALLOWED_ORIGINS } from './config/cors.js'; import { PORT } from './config/deployedContext.js'; import { createServerAdapter } from '@whatwg-node/server'; -import { error, json, AutoRouter, cors, IRequest } from 'itty-router'; +import { error, AutoRouter, cors, IRequest } from 'itty-router'; import { authRouter } from './routers/auth.js'; import { migrateToLatest } from '@biscuits/db'; import { verdantRouter } from './routers/verdant.js'; diff --git a/web/package.json b/web/package.json index d92d5098..962dd221 100644 --- a/web/package.json +++ b/web/package.json @@ -9,7 +9,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@a-type/auth-client": "1.0.4", + "@a-type/auth-client": "1.0.6", "@a-type/ui": "^0.8.20", "@biscuits/apps": "workspace:*", "@biscuits/client": "workspace:*", @@ -33,7 +33,7 @@ "three": "0.165.0" }, "devDependencies": { - "@0no-co/graphqlsp": "^1.3.4", + "@0no-co/graphqlsp": "1.12.8", "@originjs/vite-plugin-commonjs": "^1.0.3", "@types/cookie": "^0.5.4", "@types/node": "20.12.7",