diff --git a/app/javascript/wardrobe-2020/AppProvider.js b/app/javascript/wardrobe-2020/AppProvider.js index 5b2c58f2..a47a20d4 100644 --- a/app/javascript/wardrobe-2020/AppProvider.js +++ b/app/javascript/wardrobe-2020/AppProvider.js @@ -1,10 +1,8 @@ import React from "react"; import * as Sentry from "@sentry/react"; import { Integrations } from "@sentry/tracing"; -import { Auth0Provider } from "@auth0/auth0-react"; import { ChakraProvider, Box, useColorModeValue } from "@chakra-ui/react"; import { ApolloProvider } from "@apollo/client"; -import { useAuth0 } from "@auth0/auth0-react"; import { BrowserRouter } from "react-router-dom"; import { Global } from "@emotion/react"; @@ -15,46 +13,23 @@ export default function AppProvider({ children }) { return ( - - - - {children} - - - + + + {children} + + ); } function DTIApolloProvider({ children, additionalCacheState = {} }) { - const auth0 = useAuth0(); - const auth0Ref = React.useRef(auth0); - - React.useEffect(() => { - auth0Ref.current = auth0; - }, [auth0]); - // Save the first `additionalCacheState` we get as our `initialCacheState`, // which we'll use to initialize the client without having to wait a tick. const [initialCacheState, unusedSetInitialCacheState] = React.useState(additionalCacheState); const client = React.useMemo( - () => - buildApolloClient({ - getAuth0: () => auth0Ref.current, - initialCacheState, - }), + () => buildApolloClient({ initialCacheState }), [initialCacheState], ); diff --git a/app/javascript/wardrobe-2020/apolloClient.js b/app/javascript/wardrobe-2020/apolloClient.js index 9676f08e..45b579ae 100644 --- a/app/javascript/wardrobe-2020/apolloClient.js +++ b/app/javascript/wardrobe-2020/apolloClient.js @@ -3,8 +3,6 @@ import { loadErrorMessages, loadDevMessages } from "@apollo/client/dev"; import { setContext } from "@apollo/client/link/context"; import { createPersistedQueryLink } from "apollo-link-persisted-queries"; -import { getAuthModeFeatureFlag } from "./components/useCurrentUser"; - // Use Apollo's error messages in development. if (process.env["NODE_ENV"] === "development") { loadErrorMessages(); @@ -162,73 +160,19 @@ const typePolicies = { const httpLink = createHttpLink({ uri: "https://impress-2020.openneo.net/api/graphql", }); -const buildAuthLink = (getAuth0) => - setContext(async (_, { headers = {}, sendAuth = false }) => { - if (!sendAuth) { - return; - } - const token = await getAccessToken(getAuth0); - if (token) { - return { - headers: { - ...headers, - authorization: token ? `Bearer ${token}` : "", - }, - }; - } - }); - -// This is a temporary way to pass the DTIAuthMode feature flag back to the -// server! -const authModeLink = setContext((_, { headers = {} }) => { - const authMode = getAuthModeFeatureFlag(); - return { - headers: { - ...headers, - "DTI-Auth-Mode": authMode, - }, - }; -}); - -async function getAccessToken(getAuth0) { - // Wait for auth0 to stop loading, so we can maybe get a token! - // We'll do this hackily by checking every 100ms until it's true. - await new Promise((resolve) => { - function check() { - if (getAuth0().isLoading) { - setTimeout(check, 100); - } else { - resolve(); - } - } - check(); - }); - - const { isAuthenticated, getAccessTokenSilently } = getAuth0(); - if (isAuthenticated) { - const token = await getAccessTokenSilently(); - return token; - } -} - -const buildLink = (getAuth0) => - buildAuthLink(getAuth0) - .concat(authModeLink) - .concat( - createPersistedQueryLink({ - useGETForHashedQueries: true, - }), - ) - .concat(httpLink); +const buildLink = () => + createPersistedQueryLink({ + useGETForHashedQueries: true, + }).concat(httpLink); /** * apolloClient is the global Apollo Client instance we use for GraphQL * queries. This is how we communicate with the server! */ -const buildClient = ({ getAuth0, initialCacheState }) => { +const buildClient = ({ initialCacheState }) => { return new ApolloClient({ - link: buildLink(getAuth0), + link: buildLink(), cache: new InMemoryCache({ typePolicies }).restore(initialCacheState), connectToDevTools: true, }); diff --git a/app/javascript/wardrobe-2020/components/useCurrentUser.js b/app/javascript/wardrobe-2020/components/useCurrentUser.js index 4a47f656..19b3fc90 100644 --- a/app/javascript/wardrobe-2020/components/useCurrentUser.js +++ b/app/javascript/wardrobe-2020/components/useCurrentUser.js @@ -1,5 +1,4 @@ import { gql, useMutation, useQuery } from "@apollo/client"; -import { useAuth0 } from "@auth0/auth0-react"; import { useLocalStorage } from "../util"; const NOT_LOGGED_IN_USER = { @@ -10,13 +9,7 @@ const NOT_LOGGED_IN_USER = { }; function useCurrentUser() { - const [authMode] = useAuthModeFeatureFlag(); - const currentUserViaAuth0 = useCurrentUserViaAuth0({ - isEnabled: authMode === "auth0", - }); - const currentUserViaDb = useCurrentUserViaDb({ - isEnabled: authMode === "db", - }); + const currentUser = useCurrentUserQuery(); // In development, you can start the server with // `IMPRESS_LOG_IN_AS=12345 vc dev` to simulate logging in as user 12345. @@ -39,42 +32,10 @@ function useCurrentUser() { }; } - if (authMode === "auth0") { - return currentUserViaAuth0; - } else if (authMode === "db") { - return currentUserViaDb; - } else { - console.error(`Unexpected auth mode: ${JSON.stringify(authMode)}`); - return NOT_LOGGED_IN_USER; - } + return currentUser; } -function useCurrentUserViaAuth0({ isEnabled }) { - // NOTE: I don't think we can actually, by the rule of hooks, *not* ask for - // Auth0 login state when `isEnabled` is false, because `useAuth0` - // doesn't accept a similar parameter to disable itself. We'll just - // accept the redundant network effort during rollout, then delete it - // when we're done. (So, the param isn't actually doing a whole lot; I - // mostly have it for consistency with `useCurrentUserViaDb`, to make - // it clear where the real difference is.) - const { isLoading, isAuthenticated, user } = useAuth0(); - - if (!isEnabled) { - return NOT_LOGGED_IN_USER; - } else if (isLoading) { - return { ...NOT_LOGGED_IN_USER, isLoading: true }; - } else if (!isAuthenticated) { - return NOT_LOGGED_IN_USER; - } else { - return { - isLoading: false, - isLoggedIn: true, - ...getUserInfoFromAuth0Data(user), - }; - } -} - -function useCurrentUserViaDb({ isEnabled }) { +function useCurrentUserQuery() { const { loading, data } = useQuery( gql` query useCurrentUser { @@ -85,7 +46,6 @@ function useCurrentUserViaDb({ isEnabled }) { } `, { - skip: !isEnabled, onError: (error) => { // On error, we don't report anything to the user, but we do keep a // record in the console. We figure that most errors are likely to be @@ -99,9 +59,7 @@ function useCurrentUserViaDb({ isEnabled }) { }, ); - if (!isEnabled) { - return NOT_LOGGED_IN_USER; - } else if (loading) { + if (loading) { return { ...NOT_LOGGED_IN_USER, isLoading: true }; } else if (data?.currentUser == null) { return NOT_LOGGED_IN_USER; @@ -115,123 +73,4 @@ function useCurrentUserViaDb({ isEnabled }) { } } -function getUserInfoFromAuth0Data(user) { - return { - id: user.sub?.match(/^auth0\|impress-([0-9]+)$/)?.[1], - username: user["https://oauth.impress-2020.openneo.net/username"], - }; -} - -/** - * useLoginActions returns a `startLogin` function to start login with Auth0, - * and a `logout` function to logout from whatever auth mode is in use. - * - * Note that `startLogin` is only supported with the Auth0 auto mode. In db - * mode, you should open a `LoginModal` instead! - */ -export function useLogout() { - const { logout: logoutWithAuth0 } = useAuth0(); - const [authMode] = useAuthModeFeatureFlag(); - - const [sendLogoutMutation, { loading, error }] = useMutation( - gql` - mutation useLogout_Logout { - logout { - id - } - } - `, - { - update: (cache, { data }) => { - // Evict the `currentUser` from the cache, which will force all queries - // on the page that depend on it to update. (This includes the - // GlobalHeader that shows who you're logged in as!) - // - // We also evict the user themself, to force-update things that we're - // allowed to see about this user (e.g. private lists). - // - // I don't do any optimistic UI here, because auth is complex enough - // that I'd rather only show logout success after validating it through - // an actual server round-trip. - cache.evict({ id: "ROOT_QUERY", fieldName: "currentUser" }); - if (data.logout?.id != null) { - cache.evict({ id: `User:${data.logout.id}` }); - } - cache.gc(); - }, - }, - ); - - const logoutWithDb = () => { - sendLogoutMutation().catch((e) => {}); // handled in error UI - }; - - if (authMode === "auth0") { - return [logoutWithAuth0, { loading: false, error: null }]; - } else if (authMode === "db") { - return [logoutWithDb, { loading, error }]; - } else { - console.error(`unexpected auth mode: ${JSON.stringify(authMode)}`); - return [() => {}, { loading: false, error: null }]; - } -} - -/** - * useAuthModeFeatureFlag returns "db" by default, but "auto" if you're falling - * back to the old auth0-backed login mode. - * - * To set this manually, click "Better login system" on the homepage in the - * Coming Soon block, and switch the toggle. - */ -export function useAuthModeFeatureFlag() { - // We'll probably add a like, experimental gradual rollout thing here too. - // But for now we just check your device's local storage! (This is why we - // default to `null` instead of "auth0", I want to be unambiguous that this - // is the *absence* of a localStorage value, and not risk accidentally - // setting this override value to auth0 on everyone's devices 😅) - let [savedValue, setSavedValue] = useLocalStorage( - "DTIAuthModeFeatureFlag", - null, - ); - - if (!["auth0", "db", null].includes(savedValue)) { - console.warn( - `Unexpected DTIAuthModeFeatureFlag value: %o. Ignoring.`, - savedValue, - ); - savedValue = null; - } - - const value = savedValue || "db"; - - return [value, setSavedValue]; -} - -/** - * getAuthModeFeatureFlag returns the authMode at the time it's called. - * It's generally preferable to use `useAuthModeFeatureFlag` in a React - * setting, but we use this instead for Apollo stuff! - */ -export function getAuthModeFeatureFlag() { - const savedValueString = localStorage.getItem("DTIAuthModeFeatureFlag"); - - let savedValue; - try { - savedValue = JSON.parse(savedValueString); - } catch (error) { - console.warn(`DTIAuthModeFeatureFlag was not valid JSON. Ignoring.`); - savedValue = null; - } - - if (!["auth0", "db", null].includes(savedValue)) { - console.warn( - `Unexpected DTIAuthModeFeatureFlag value: %o. Ignoring.`, - savedValue, - ); - savedValue = null; - } - - return savedValue || "db"; -} - export default useCurrentUser; diff --git a/package.json b/package.json index f341db16..0d04821c 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,6 @@ "name": "app", "dependencies": { "@apollo/client": "^3.6.9", - "@auth0/auth0-react": "^1.0.0", "@chakra-ui/icons": "^1.0.4", "@chakra-ui/react": "^1.6.0", "@emotion/react": "^11.1.4", diff --git a/yarn.lock b/yarn.lock index a99ac12b..a841d9b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21,26 +21,6 @@ tslib "^2.3.0" zen-observable-ts "^1.2.5" -"@auth0/auth0-react@^1.0.0": - version "1.12.1" - resolved "https://registry.yarnpkg.com/@auth0/auth0-react/-/auth0-react-1.12.1.tgz#3a469518828137339c635434ab953e206e798fe8" - integrity sha512-8+ecK/4rE0AGsxLW2IDcr1oPbT55tuE6cQEzEIOkQjB6QGQxxWMzQy0D4nMKw3JUAc7nYcFVOABNFNbc471n9Q== - dependencies: - "@auth0/auth0-spa-js" "^1.22.6" - -"@auth0/auth0-spa-js@^1.22.6": - version "1.22.6" - resolved "https://registry.yarnpkg.com/@auth0/auth0-spa-js/-/auth0-spa-js-1.22.6.tgz#482c7cf546649e856020d20843cd496258bd9fa0" - integrity sha512-iL3O0vWanfKFVgy1J2ZHDPlAUK6EVHWEHWS6mUXwHEuPiK39tjlQtyUKQIJI1F5YsZB75ijGgRWMTawSDXlwCA== - dependencies: - abortcontroller-polyfill "^1.7.3" - browser-tabs-lock "^1.2.15" - core-js "^3.25.4" - es-cookie "~1.3.2" - fast-text-encoding "^1.0.6" - promise-polyfill "^8.2.3" - unfetch "^4.2.0" - "@babel/code-frame@^7.0.0": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.10.tgz#1c20e612b768fefa75f6e90d6ecb86329247f0a3" @@ -1078,11 +1058,6 @@ dependencies: tslib "^2.3.0" -abortcontroller-polyfill@^1.7.3: - version "1.7.5" - resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz#6738495f4e901fbb57b6c0611d0c75f76c485bed" - integrity sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ== - ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -1134,13 +1109,6 @@ babel-plugin-macros@^3.1.0: cosmiconfig "^7.0.0" resolve "^1.19.0" -browser-tabs-lock@^1.2.15: - version "1.3.0" - resolved "https://registry.yarnpkg.com/browser-tabs-lock/-/browser-tabs-lock-1.3.0.tgz#4c0381b47b55cd29feaaea5846b7bb97aac76053" - integrity sha512-g6nHaobTiT0eMZ7jh16YpD2kcjAp+PInbiVq3M1x6KKaEIVhT4v9oURNIpZLOZ3LQbQ3XYfNhMAb/9hzNLIWrw== - dependencies: - lodash ">=4.17.21" - callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -1184,11 +1152,6 @@ copy-to-clipboard@3.3.1: dependencies: toggle-selection "^1.0.6" -core-js@^3.25.4: - version "3.32.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.32.0.tgz#7643d353d899747ab1f8b03d2803b0312a0fb3b6" - integrity sha512-rd4rYZNlF3WuoYuRIDEmbR/ga9CeuWX9U05umAvgrrZoHY4Z++cp/xwPQMvUpBB4Ag6J8KfD80G0zwCyaSxDww== - cosmiconfig@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" @@ -1242,11 +1205,6 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-cookie@~1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/es-cookie/-/es-cookie-1.3.2.tgz#80e831597f72a25721701bdcb21d990319acd831" - integrity sha512-UTlYYhXGLOy05P/vKVT2Ui7WtC7NiRzGtJyAKKn32g5Gvcjn7KAClLPWlipCtxIus934dFg9o9jXiBL0nP+t9Q== - es6-promise@^4.2.8: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" @@ -1295,11 +1253,6 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-text-encoding@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz#0aa25f7f638222e3396d72bf936afcf1d42d6867" - integrity sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w== - find-root@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" @@ -1443,11 +1396,6 @@ lodash.mergewith@4.6.2: resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== -lodash@>=4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -1528,11 +1476,6 @@ prettier@^3.0.3: resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.3.tgz#432a51f7ba422d1469096c0fdc28e235db8f9643" integrity sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg== -promise-polyfill@^8.2.3: - version "8.3.0" - resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.3.0.tgz#9284810268138d103807b11f4e23d5e945a4db63" - integrity sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg== - prop-types@^15.6.2, prop-types@^15.7.2: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" @@ -1785,11 +1728,6 @@ tweenjs@^1.0.2: resolved "https://registry.yarnpkg.com/tweenjs/-/tweenjs-1.0.2.tgz#768869c4d4ba91fdb4c5ccc661969c58138555af" integrity sha512-WnFozCNkUkmJtLqJyGrToxVojW2Srzudktr8BzFKQijQRVcmlq7Fc+qfo75ccnwIJGiRWbXKfg7qU67Tzbb1bg== -unfetch@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be" - integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA== - use-callback-ref@^1.2.3, use-callback-ref@^1.2.5: version "1.3.0" resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.0.tgz#772199899b9c9a50526fedc4993fc7fa1f7e32d5"