diff --git a/SETUP.md b/SETUP.md index 02b6ae0..2a726e9 100644 --- a/SETUP.md +++ b/SETUP.md @@ -31,10 +31,10 @@ then also click __"Continue"__ a couple time to navigate to __"Scopes"__. and here we need to __"+ Add"__ the following scopes: * Meeting - * View all user meetings (*meeting:read:admin*) - * View and manage all user meetings (*meeting:write:admin*) + * View all user meetings (*meeting:read:meeting:admin*, *meeting:read:list_meetings:admin*) + * View and manage all user meetings (*meeting:write:meeting:admin*, *meeting:delete:meeting:admin*) * User - * View all user information (*user:read:admin*) + * View all user information (*user:read:user:admin*) Ok, head back to Deskpro and enter your __Client ID__ and __Client secret__ into the app settings form. diff --git a/manifest.json b/manifest.json index 1e53829..732da62 100644 --- a/manifest.json +++ b/manifest.json @@ -9,19 +9,31 @@ "hasDevMode": true, "serveUrl": "https://apps-cdn.deskpro-service.com/__name__/__version__", "targets": [{ "target": "ticket_sidebar", "entrypoint": "index.html" }], + "secrets": "SMEkhJoGvqgiXxPNDne4o+YHmr6X/fsKwQ8Gbzs6gGZAlnyql+HGxW7rqIg7flz0+bU+tdKnt40l3SnK1acmdySlaTlBI/Jvt/cHTsp56lxaArYy44PG2FrgxMcrk6EXUZHWORVHDZ6kcg1PwiAb5bBsOw8DH4tvn+Rm9WVewyaQPlVXnzKhqU/HrFE8KJjTKnfVyBprx3KBxoc/1Y3GBwsMs40Q51NjTnKMj2b3X+o+srknWcU/c4VvYGE8lIYYb6erBLYFGs5koa4UysmKigk2V4FUTpHTYWdS3hI/jQuvtpIj7/Zy1XXrBtLEt12st1iD8MO3vBYcYPR4bsopgSnBHIr8qXkNPLSlJe41NLwp3O4ctJcTY56yulOtjntYtYVWyLfUNioACnqUZujwbQiZeuxCD/nyRBZ0kV+/UmunQG+FDBxLhRfF5M6Ns5cF8ZkGu7eSfwURzLWPx14T7Poqdbs1AKwpO8NVVqKS+pchXWZucA2uP/LlXxJS430uOGjGRphli/FpwgX3Or2XAoOWAGMOUsOvttHD/V7PeGDkR8CpDJoVmkn+yyboj7kXh6foJf5o7v64jd5nNAVy0FYW518OOp/r3eGybcGqgxFI7bX9rhP62cALDc4ajFe5hoL64e4gLP3orn2EkejFbjG1BWfudZYhhgjlF8IywxI=", "settings": { + "use_advanced_connect": { + "title": "Advanced Connect", + "description": "Follow the setup guide and use your credentials to connect the app to Deskpro.", + "type": "boolean", + "default": false, + "isRequired": false, + "isBackendOnly": false, + "order": 5 + }, "client_id": { "title": "Client ID", "type": "string", - "isRequired": true, + "isRequired": false, "isBackendOnly": false, + "condition": "settings.use_advanced_connect != false", "order": 10 }, "client_secret": { "title": "Client secret", "type": "string", - "isRequired": true, + "isRequired": false, "isBackendOnly": true, + "condition": "settings.use_advanced_connect != false", "order": 20 }, "callback_url": { @@ -30,6 +42,7 @@ "options": { "entrypoint": "#/admin/callback", "height": "75px" }, "isRequired": false, "isBackendOnly": true, + "condition": "settings.use_advanced_connect != false", "order": 30 } }, diff --git a/package.json b/package.json index 4ce26bd..1aff7be 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,9 @@ "license": "BSD-3-Clause", "scripts": { "start": "vite", - "build": "rimraf ./dist/* && tsc && vite build", + "build": "rm -rf ./dist/ && tsc && vite build", "build:package": "pnpm run build && pnpm run package", - "package": "rimraf ./build/* && node ./bin/package.js", + "package": "rm -rf ./build/ && node ./bin/package.js", "serve": "vite preview", "lint": "eslint --max-warnings 0 --ext ts,tsx ./src", "test": "cross-env NODE_OPTIONS=--max-old-space-size=1024 jest --maxWorkers=75%", @@ -15,7 +15,7 @@ "bumpManifestVer": "node ./bin/bumpManifestVer.js" }, "dependencies": { - "@deskpro/app-sdk": "^5.1.1", + "@deskpro/app-sdk": "^6.0.3", "@deskpro/deskpro-ui": "^8.2.0", "@heroicons/react": "1.0.6", "@hookform/resolvers": "^3.0.1", @@ -68,7 +68,6 @@ "jest-environment-jsdom": "^29.7.0", "node-fetch": "^3.3.0", "prettier": "^2.8.8", - "rimraf": "^3.0.2", "rollup-plugin-copy": "3.4.0", "slugify": "^1.6.5", "ts-jest": "^27.1.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fd09fe1..d319f87 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@deskpro/app-sdk': - specifier: ^5.1.1 - version: 5.1.1(@deskpro/deskpro-ui@8.2.1(@types/web@0.0.99)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + specifier: ^6.0.3 + version: 6.0.3(@deskpro/deskpro-ui@8.2.1(@types/web@0.0.99)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) '@deskpro/deskpro-ui': specifier: ^8.2.0 version: 8.2.1(@types/web@0.0.99)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) @@ -162,9 +162,6 @@ importers: prettier: specifier: ^2.8.8 version: 2.8.8 - rimraf: - specifier: ^3.0.2 - version: 3.0.2 rollup-plugin-copy: specifier: 3.4.0 version: 3.4.0 @@ -383,8 +380,8 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@deskpro/app-sdk@5.1.1': - resolution: {integrity: sha512-GVwjVb/8EX4aBHtDu7YqWKpVPxslyFy0nAEjbo59hycy73KPqV0UdOCQNM5LasZnrC1U6epV/BYVQrUDplTzuw==} + '@deskpro/app-sdk@6.0.3': + resolution: {integrity: sha512-7IYRxJ6SRCKrsSFO5bZwGYhRoEvX08LPv0pDGIxhNejrqp+eaDb8hjobKnPKwK22pRNJ/riExGPsbGXeSuuCZQ==} engines: {node: '>=20.0.0'} peerDependencies: '@deskpro/deskpro-ui': ^8.0.0 @@ -2364,8 +2361,8 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - libphonenumber-js@1.11.17: - resolution: {integrity: sha512-Jr6v8thd5qRlOlc6CslSTzGzzQW03uiscab7KHQZX1Dfo4R6n6FDhZ0Hri6/X7edLIDv9gl4VMZXhxTjLnl0VQ==} + libphonenumber-js@1.11.19: + resolution: {integrity: sha512-bW/Yp/9dod6fmyR+XqSUL1N5JE7QRxQ3KrBIbYS1FTv32e5i3SEtQVX+71CYNv8maWNSOgnlCoNp9X78f/cKiA==} lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -3548,7 +3545,7 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} - '@deskpro/app-sdk@5.1.1(@deskpro/deskpro-ui@8.2.1(@types/web@0.0.99)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)': + '@deskpro/app-sdk@6.0.3(@deskpro/deskpro-ui@8.2.1(@types/web@0.0.99)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)': dependencies: '@deskpro/deskpro-ui': 8.2.1(@types/web@0.0.99)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) '@fortawesome/fontawesome-svg-core': 6.7.2 @@ -3564,7 +3561,7 @@ snapshots: fuse.js: 7.0.0 handlebars: 4.7.7 i18n-iso-countries: 7.13.0 - libphonenumber-js: 1.11.17 + libphonenumber-js: 1.11.19 modern-normalize: 1.1.0 penpal: 6.2.1 react: 18.3.1 @@ -5935,7 +5932,7 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - libphonenumber-js@1.11.17: {} + libphonenumber-js@1.11.19: {} lines-and-columns@1.2.4: {} diff --git a/src/pages/AdminPage/AdminPage.tsx b/src/pages/AdminPage/AdminPage.tsx index c091ab7..368a26d 100644 --- a/src/pages/AdminPage/AdminPage.tsx +++ b/src/pages/AdminPage/AdminPage.tsx @@ -1,6 +1,5 @@ -import { useState, useMemo } from "react"; +import { useState } from "react"; import styled from "styled-components"; -import { v4 as uuidv4 } from "uuid"; import { P1 } from "@deskpro/deskpro-ui"; import { LoadingSpinner, @@ -8,6 +7,7 @@ import { } from "@deskpro/app-sdk"; import { CopyToClipboardInput } from "@/components/common"; import type { FC } from "react"; +import type { OAuth2Result } from "@deskpro/app-sdk"; const Description = styled(P1)` margin-top: 8px; @@ -17,14 +17,22 @@ const Description = styled(P1)` const AdminPage: FC = () => { const [callbackUrl, setCallbackUrl] = useState(null); - const key = useMemo(() => uuidv4(), []); + useInitialisedDeskproAppClient(async (client) => { + const oauth2 = await client.startOauth2Local( + ({ state, callbackUrl }) => `https://zoom.us/oauth/authorize?response_type=code&client_id=xxx&state=${state}&redirect_uri=${callbackUrl}`, + /code=(?[0-9a-f]+)/, + async (): Promise => ({ data: { access_token: "", refresh_token: "" } }) + ); - useInitialisedDeskproAppClient((client) => { - client.oauth2() - .getAdminGenericCallbackUrl(key, /code=(?[0-9a-f]+)/, /state=(?.+)/) - .then(({ callbackUrl }) => setCallbackUrl(callbackUrl)); - }, [key]); + // Extract the callback URL from the authorization URL + const url = new URL(oauth2.authorizationUrl); + const redirectUri = url.searchParams.get("redirect_uri"); + + if (redirectUri) { + setCallbackUrl(redirectUri); + } + }); if (!callbackUrl) { return (); @@ -38,4 +46,4 @@ const AdminPage: FC = () => { ); }; -export { AdminPage }; +export { AdminPage }; \ No newline at end of file diff --git a/src/pages/LoginPage/LoginPage.tsx b/src/pages/LoginPage/LoginPage.tsx index 7142811..60cbc29 100644 --- a/src/pages/LoginPage/LoginPage.tsx +++ b/src/pages/LoginPage/LoginPage.tsx @@ -1,20 +1,13 @@ -import { useEffect } from "react"; -import { useNavigate } from "react-router-dom"; -import { H3 } from "@deskpro/deskpro-ui"; -import { Title, useDeskproElements } from "@deskpro/app-sdk"; -import { useSetTitle } from "@/hooks"; +import { AnchorButton } from "@/components/common"; +import { ErrorBlock } from "@/components"; +import { H3, Stack } from "@deskpro/deskpro-ui"; +import { useDeskproElements } from "@deskpro/app-sdk"; import { useLogin } from "./hooks"; -import { Container, AnchorButton } from "@/components/common"; +import { useSetTitle } from "@/hooks"; import type { FC } from "react"; const LoginPage: FC = () => { - const navigate = useNavigate(); - const { - isAuth, - authLink, - onSignIn, - isLoading, - } = useLogin(); + const { onSignIn, authUrl, isLoading, error } = useLogin(); useSetTitle("Zoom Meetings"); @@ -23,25 +16,21 @@ const LoginPage: FC = () => { registerElement("refresh", { type: "refresh_button" }); }); - useEffect(() => { - if (isAuth) { - navigate("/home"); - } - }, [isAuth, navigate]); - return ( - - + <Stack padding={12} vertical gap={12} role="alert"> + <H3>Log into your Zoom Account.</H3> <AnchorButton intent="secondary" text="Log In" target="_blank" - href={authLink} + href={authUrl ?? "#"} onClick={onSignIn} loading={isLoading} - disabled={isLoading} + disabled={!authUrl || isLoading} /> - </Container> + + {error && (<div style={{ width: "100%" }}><ErrorBlock text={error} /></div>)} + </Stack> ); }; diff --git a/src/pages/LoginPage/__tests__/LoginPage.test.tsx b/src/pages/LoginPage/__tests__/LoginPage.test.tsx index 62cacf2..131a119 100644 --- a/src/pages/LoginPage/__tests__/LoginPage.test.tsx +++ b/src/pages/LoginPage/__tests__/LoginPage.test.tsx @@ -1,9 +1,8 @@ -import { useNavigate } from "react-router-dom"; import { act, waitFor } from "@testing-library/react"; -import userEvent from "@testing-library/user-event"; import { LoginPage } from "../LoginPage"; import { render } from "@/testing"; import { useLogin } from "../hooks"; +import userEvent from "@testing-library/user-event"; jest.mock("react-router-dom", () => ({ ...jest.requireActual("react-router-dom"), @@ -15,12 +14,11 @@ describe("LoginPage", () => { test("render", async () => { (useLogin as jest.Mock).mockImplementation(() => ({ error: null, - isAuth: false, - authLink: "https://call-back.url/?code=123", + authUrl: "https://call-back.url/?code=123", onSignIn: jest.fn(), isLoading: false, })); - const { findByRole } = render(<LoginPage/>, { wrappers: { theme: true }}); + const { findByRole } = render(<LoginPage />, { wrappers: { theme: true } }); const loginButton = await findByRole("link", { name: /Log In/i }); await waitFor(() => { @@ -32,12 +30,11 @@ describe("LoginPage", () => { const onSignIn = jest.fn(); (useLogin as jest.Mock).mockImplementation(() => ({ error: null, - isAuth: false, isLoading: false, - authLink: "https://call-back.url/?code=123", + authUrl: "https://call-back.url/?code=123", onSignIn, })); - const { findByRole } = render(<LoginPage/>, { wrappers: { theme: true }}); + const { findByRole } = render(<LoginPage />, { wrappers: { theme: true } }); const loginButton = await findByRole("link", { name: /Log In/i }); @@ -47,20 +44,4 @@ describe("LoginPage", () => { expect(onSignIn).toHaveBeenCalledTimes(1); }); - - test("should navigate to /home after auth", async () => { - const navigate = jest.fn(); - (useLogin as jest.Mock).mockImplementation(() => ({ - error: null, - isAuth: true, - isLoading: false, - authLink: "https://call-back.url/?code=123", - onSignIn: jest.fn(), - })); - (useNavigate as jest.Mock).mockImplementation(() => navigate); - render(<LoginPage/>, { wrappers: { theme: true }}); - - expect(navigate).toHaveBeenCalledTimes(1); - expect(navigate).toHaveBeenCalledWith("/home"); - }); }); diff --git a/src/pages/LoginPage/hooks.ts b/src/pages/LoginPage/hooks.ts index 1ca5331..47a5497 100644 --- a/src/pages/LoginPage/hooks.ts +++ b/src/pages/LoginPage/hooks.ts @@ -1,105 +1,123 @@ -import { useState, useEffect, useMemo, useCallback } from "react"; -import { createSearchParams } from "react-router-dom"; -import { v4 as uuidv4 } from "uuid"; -import { - useDeskproAppClient, - useDeskproLatestAppContext, -} from "@deskpro/app-sdk"; -import { useAsyncError } from "@/hooks"; -import { setAccessTokenService, setRefreshTokenService } from "@/services/deskpro"; -import { - isAccessToken, - isErrorMessage, - getAccessTokenService, - getCurrentUserService, -} from "@/services/zoom"; import { defaultLoginError } from "./constants"; -import type { OAuth2StaticCallbackUrl } from "@deskpro/app-sdk"; +import { isAccessToken, isErrorMessage, getAccessTokenService, getCurrentUserService } from "@/services/zoom"; +import { setAccessTokenService, setRefreshTokenService } from "@/services/deskpro"; +import { useCallback, useState } from "react"; +import { useDeskproLatestAppContext, useInitialisedDeskproAppClient } from "@deskpro/app-sdk"; +import { useNavigate } from "react-router-dom"; +import type { IOAuth2, OAuth2Result } from "@deskpro/app-sdk"; import type { TicketData, Settings } from "@/types"; -type UseLogin = () => { - isAuth: boolean; - authLink: string; - isLoading: boolean; - onSignIn: () => void; +interface UseLogin { + onSignIn: () => void, + authUrl: string | null, + error: null | string, + isLoading: boolean, }; -const useLogin: UseLogin = () => { - const key = useMemo(() => uuidv4(), []); - const { client } = useDeskproAppClient(); - const { context } = useDeskproLatestAppContext<TicketData, Settings>(); - const { asyncErrorHandler } = useAsyncError(); +export function useLogin(): UseLogin { + const [authUrl, setAuthUrl] = useState<string | null>(null) + const [error, setError] = useState<null | string>(null) + const [isLoading, setIsLoading] = useState(false) + const [isPolling, setIsPolling] = useState(false) + const [oAuth2Context, setOAuth2Context] = useState<IOAuth2 | null>(null) - const [isAuth, setIsAuth] = useState<boolean>(false); - const [authLink, setAuthLink] = useState<string>(""); - const [isLoading, setIsLoading] = useState<boolean>(true); - const [callback, setCallback] = useState< - OAuth2StaticCallbackUrl | undefined - >(); - const clientId = context?.settings?.client_id; - const callbackUrl = callback?.callbackUrl; + const navigate = useNavigate() + const { context } = useDeskproLatestAppContext<TicketData, Settings>(); - const onSignIn = useCallback(() => { - if (!client || !callback?.poll || !callback.callbackUrl) { + useInitialisedDeskproAppClient(async (client) => { + if (context?.settings === undefined) { + // Make sure settings have loaded. + return; + } + const clientId = context?.settings.client_id; + const mode = context?.settings.use_advanced_connect === false ? 'global' : 'local'; + if (mode === 'local' && (typeof clientId !== 'string' || clientId.trim() === "")) { + // Local mode requires a clientId. + setError("A client ID is required"); return; } - setTimeout(() => setIsLoading(true), 1000); + const oAuth2Response = mode === 'local' + ? await client.startOauth2Local( + ({ state, callbackUrl }) => `https://zoom.us/oauth/authorize?response_type=code&client_id=${clientId}&state=${state}&redirect_uri=${callbackUrl}`, + /code=(?<code>[\d\w-]+)/, + async (code: string): Promise<OAuth2Result> => { + // Extract the callback URL from the authorization URL + const url = new URL(oAuth2Response.authorizationUrl); + const redirectUri = url.searchParams.get("redirect_uri"); - callback - .poll() - .then(({ token }) => getAccessTokenService(client, token, callback.callbackUrl)) - .then((data) => isAccessToken(data) - ? Promise.all([setAccessTokenService(client, data), setRefreshTokenService(client, data)]) - : Promise.reject(isErrorMessage(data) ? data.errorMessage : defaultLoginError) + if (!redirectUri) { + throw new Error("Failed to get callback URL"); + } + + const data = await getAccessTokenService(client, code, redirectUri); + + if (!isAccessToken(data)) { + throw new Error(isErrorMessage(data) ? data.errorMessage : defaultLoginError); + } + + return { data }; + } ) - .then(([access, refresh]) => access.isSuccess && refresh.isSuccess - ? Promise.resolve() - : Promise.reject(([] as string[]).concat(access.errors, refresh.errors))) - .then(() => getCurrentUserService(client)) - .then((user) => { - if (!user?.id) { + : await client.startOauth2Global("GKHUwXzTsa3QJsm8Dhp8w"); + + setAuthUrl(oAuth2Response.authorizationUrl); + setOAuth2Context(oAuth2Response); + }, [context?.settings.client_id, context?.settings.use_advanced_connect]); + + + useInitialisedDeskproAppClient((client) => { + if (!oAuth2Context) { + return + } + + const startPolling = async () => { + try { + const result = await oAuth2Context.poll(); + + const [access, refresh] = await Promise.all([ + setAccessTokenService(client, result.data.access_token), + result.data.refresh_token + ? setRefreshTokenService(client, result.data.refresh_token) + : Promise.resolve({ isSuccess: true, errors: [], }), + ]); + + if (!access.isSuccess || !refresh.isSuccess) { + throw new Error(([] as string[]).concat(access.errors, refresh.errors).join(", ")); + } + + try { + const user = await getCurrentUserService(client); + if (!user?.id) { + throw new Error() + } + } catch { throw new Error("Can't find current user"); } - setIsAuth(true); - }) - .catch(asyncErrorHandler) - .finally(() => setIsLoading(false)); - }, [callback, client, setIsLoading, asyncErrorHandler]); - - /** set callback */ - useEffect(() => { - if (!callback && client) { - client - .oauth2() - .getGenericCallbackUrl( - key, - /code=(?<token>[\d\w-]+)/, - /state=(?<key>.+)/ - ) - .then(setCallback); + + navigate("/home") + + } catch (error) { + setError(error instanceof Error ? error.message : "Unknown error"); + + } finally { + setIsLoading(false) + setIsPolling(false) + } } - }, [client, key, callback]); - - /** set authLink */ - useEffect(() => { - if (key && callbackUrl && clientId) { - setAuthLink( - `https://zoom.us/oauth/authorize?${createSearchParams([ - ["response_type", "code"], - ["client_id", clientId], - ["state", key], - ["redirect_uri", callbackUrl], - ])}` - ); - setIsLoading(false); - } else { - setAuthLink(""); - setIsLoading(true); + + if (isPolling) { + void startPolling() } - }, [key, callbackUrl, clientId]); + }, [isPolling, oAuth2Context, navigate]) + + + const onSignIn = useCallback(() => { + setIsLoading(true); + setIsPolling(true); + window.open(authUrl ?? "", '_blank'); + }, [setIsLoading, authUrl]); - return { isAuth, authLink, onSignIn, isLoading }; -}; -export { useLogin }; + return { authUrl, onSignIn, error, isLoading } +}; \ No newline at end of file diff --git a/src/services/deskpro/setAccessTokenService.ts b/src/services/deskpro/setAccessTokenService.ts index 1eeadd4..f4f61c1 100644 --- a/src/services/deskpro/setAccessTokenService.ts +++ b/src/services/deskpro/setAccessTokenService.ts @@ -1,8 +1,7 @@ import { ACCESS_TOKEN_PATH } from "@/constants"; import type { IDeskproClient } from "@deskpro/app-sdk"; -import type { OAuthToken } from "@/services/zoom/types"; -const setAccessTokenService = (client: IDeskproClient, { access_token }: OAuthToken) => { +const setAccessTokenService = (client: IDeskproClient, access_token: string) => { return client.setUserState(ACCESS_TOKEN_PATH, access_token, { backend: true }); }; diff --git a/src/services/deskpro/setRefreshTokenService.ts b/src/services/deskpro/setRefreshTokenService.ts index cbd763d..85cf022 100644 --- a/src/services/deskpro/setRefreshTokenService.ts +++ b/src/services/deskpro/setRefreshTokenService.ts @@ -1,8 +1,7 @@ import { REFRESH_TOKEN_PATH } from "@/constants"; import type { IDeskproClient } from "@deskpro/app-sdk"; -import type { OAuthToken } from "@/services/zoom/types"; -const setRefreshTokenService = (client: IDeskproClient, { refresh_token }: OAuthToken) => { +const setRefreshTokenService = (client: IDeskproClient, refresh_token: string) => { return client.setUserState(REFRESH_TOKEN_PATH, refresh_token, { backend: true }); }; diff --git a/src/services/zoom/baseRequest.ts b/src/services/zoom/baseRequest.ts index be87996..44e1da1 100644 --- a/src/services/zoom/baseRequest.ts +++ b/src/services/zoom/baseRequest.ts @@ -41,10 +41,11 @@ const baseRequest: Request = async (client, { let res = await dpFetch(requestUrl, options); if (res.status === 401) { + // Retry if the issue was because the access token expired. const data = await refreshTokenService(client); - await setAccessTokenService(client, data); - await setRefreshTokenService(client, data); + await setAccessTokenService(client, data.access_token); + await setRefreshTokenService(client, data.refresh_token); res = await dpFetch(requestUrl, options); } diff --git a/src/services/zoom/types.ts b/src/services/zoom/types.ts index 9489488..46237e1 100644 --- a/src/services/zoom/types.ts +++ b/src/services/zoom/types.ts @@ -1,7 +1,6 @@ import type { Dict, DateTime } from "@/types"; export type OAuthToken = { - token_type: "bearer", access_token: string, refresh_token: string, expires_in: number, diff --git a/src/types.ts b/src/types.ts index 471cfcd..38d71ad 100644 --- a/src/types.ts +++ b/src/types.ts @@ -33,6 +33,7 @@ export type Request = <T>( /** Deskpro types */ export type Settings = { + use_advanced_connect?: boolean, client_id?: string, };