From 63624a9554a17035004ff230aeccdacda01a13ea Mon Sep 17 00:00:00 2001 From: "Timothy Z." Date: Thu, 24 Apr 2025 15:59:48 +0300 Subject: [PATCH] refactor(useapplelogin): use w/out popup instead --- package-lock.json | 10 --- package.json | 1 - src/routes/Intro/Intro.js | 8 +- src/routes/Intro/useAppleLogin.ts | 123 ++++++++++++------------------ src/types/global.d.ts | 25 ------ 5 files changed, 52 insertions(+), 115 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7f2e0b955..3deeaa24f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,6 @@ "filter-invalid-dom-props": "3.0.1", "hat": "^0.0.3", "i18next": "^24.0.5", - "jwt-decode": "^4.0.0", "langs": "github:Stremio/nodejs-langs", "lodash.debounce": "4.0.8", "lodash.intersection": "4.4.0", @@ -10235,15 +10234,6 @@ "node": ">=4.0" } }, - "node_modules/jwt-decode": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", - "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", diff --git a/package.json b/package.json index c12a0b7e8..9322aae83 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,6 @@ "filter-invalid-dom-props": "3.0.1", "hat": "^0.0.3", "i18next": "^24.0.5", - "jwt-decode": "^4.0.0", "langs": "github:Stremio/nodejs-langs", "lodash.debounce": "4.0.8", "lodash.intersection": "4.4.0", diff --git a/src/routes/Intro/Intro.js b/src/routes/Intro/Intro.js index 716fe0f0d..6af8f0167 100644 --- a/src/routes/Intro/Intro.js +++ b/src/routes/Intro/Intro.js @@ -112,7 +112,7 @@ const Intro = ({ queryParams }) => { const loginWithApple = React.useCallback(() => { openLoaderModal(); startAppleLogin() - .then(({ email, token, sub, name }) => { + .then(({ token, sub, email, name }) => { core.transport.dispatch({ action: 'Ctx', args: { @@ -129,11 +129,7 @@ const Intro = ({ queryParams }) => { }) .catch((error) => { closeLoaderModal(); - if (error.error === 'popup_closed_by_user') { - dispatch({ type: 'error', error: 'Apple login popup was closed.' }); - } else { - dispatch({ type: 'error', error: error.error }); - } + dispatch({ type: 'error', error: error.message }); }); }, []); const cancelLoginWithApple = React.useCallback(() => { diff --git a/src/routes/Intro/useAppleLogin.ts b/src/routes/Intro/useAppleLogin.ts index 176387a30..f3b5fc5af 100644 --- a/src/routes/Intro/useAppleLogin.ts +++ b/src/routes/Intro/useAppleLogin.ts @@ -1,5 +1,8 @@ +// Copyright (C) 2017-2025 Smart code 203358507 + import { useCallback, useEffect, useRef } from 'react'; -import { jwtDecode, JwtPayload } from 'jwt-decode'; +import { usePlatform } from 'stremio/common'; +import hat from 'hat'; type AppleLoginResponse = { token: string; @@ -8,97 +11,71 @@ type AppleLoginResponse = { name: string; }; -type AppleSignInResponse = { - authorization: { - code?: string; - id_token: string; - state?: string; - }; - email?: string; - fullName?: { - firstName?: string; - lastName?: string; - }; -}; -type CustomJWTPayload = JwtPayload & { - email?: string; -}; +const STREMIO_URL = 'http://localhost:3001'; +const MAX_TRIES = 25; -const CLIENT_ID = 'com.stremio.services'; +const getCredentials = async (state: string): Promise => { + try { + const response = await fetch(`${STREMIO_URL}/login-apple-get-acc/${state}`); + const { user } = await response.json(); + + return Promise.resolve({ + token: user.token, + sub: user.sub, + email: user.email, + name: user.name + }); + } catch (e) { + console.error('Failed to get credentials from Apple auth', e); + return Promise.reject(e); + } +}; const useAppleLogin = (): [() => Promise, () => void] => { + const platform = usePlatform(); const started = useRef(false); + const timeout = useRef(null); - const start = useCallback((): Promise => { - return new Promise((resolve, reject) => { - if (typeof window.AppleID === 'undefined') { - reject(new Error('Apple Sign-In not loaded')); - return; - } + const start = useCallback(() => new Promise((resolve, reject) => { + started.current = true; + const state = hat(128); + let tries = 0; + platform.openExternal(`${STREMIO_URL}/login-apple/${state}`); + + const waitForCredentials = () => { if (started.current) { - reject(new Error('Apple login already in progress')); - return; + timeout.current && clearTimeout(timeout.current); + timeout.current = setTimeout(() => { + if (tries >= MAX_TRIES) + return reject(new Error('Failed to authenticate with Apple')); + + tries++; + + getCredentials(state) + .then(resolve) + .catch(waitForCredentials); + }, 2000); } + }; - started.current = true; - - window.AppleID.auth.init({ - clientId: CLIENT_ID, - scope: 'name email', - redirectURI: 'https://web.stremio.com/', - state: 'signin', - // usePopup: true, - }); - - window.AppleID.auth.signIn().then((response: AppleSignInResponse) => { - if (response.authorization) { - try { - const idToken = response.authorization.id_token; - const payload: CustomJWTPayload = jwtDecode(idToken); - const sub = payload.sub; - const email = payload.email ?? response.email ?? ''; - - let name = ''; - if (response.fullName) { - const firstName = response.fullName.firstName || ''; - const lastName = response.fullName.lastName || ''; - name = [firstName, lastName].filter(Boolean).join(' '); - } - - if (!sub) { - reject(new Error('No sub token received from Apple')); - return; - } - - resolve({ - token: idToken, - sub: sub, - email: email, - name: name, - }); - } catch (error) { - reject(new Error(`Failed to parse id_token: ${error}`)); - } - } else { - reject(new Error('No authorization received from Apple')); - } - }).catch((error) => { - reject(error); - }); - }); - }, []); + waitForCredentials(); + }), []); const stop = useCallback(() => { started.current = false; + timeout.current && clearTimeout(timeout.current); }, []); useEffect(() => { return () => stop(); }, []); - return [start, stop]; + return [ + start, + stop, + ]; }; export default useAppleLogin; diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 78152ec45..3849b8914 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -26,31 +26,6 @@ interface Chrome { declare global { var qt: Qt | undefined; var chrome: Chrome | undefined; - interface Window { - AppleID: { - auth: { - init: (config: { - clientId: string; - scope: string; - redirectURI: string; - state: string; - // usePopup: boolean; - }) => void; - signIn: () => Promise<{ - authorization: { - code?: string; - id_token: string; - state?: string; - }; - email?: string; - fullName?: { - firstName?: string; - lastName?: string; - }; - }>; - }; - }; - } } export {};