mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-04-21 11:42:05 +00:00
refactor(useapplelogin): use w/out popup instead
This commit is contained in:
parent
fb7c5642b0
commit
63624a9554
5 changed files with 52 additions and 115 deletions
10
package-lock.json
generated
10
package-lock.json
generated
|
|
@ -23,7 +23,6 @@
|
||||||
"filter-invalid-dom-props": "3.0.1",
|
"filter-invalid-dom-props": "3.0.1",
|
||||||
"hat": "^0.0.3",
|
"hat": "^0.0.3",
|
||||||
"i18next": "^24.0.5",
|
"i18next": "^24.0.5",
|
||||||
"jwt-decode": "^4.0.0",
|
|
||||||
"langs": "github:Stremio/nodejs-langs",
|
"langs": "github:Stremio/nodejs-langs",
|
||||||
"lodash.debounce": "4.0.8",
|
"lodash.debounce": "4.0.8",
|
||||||
"lodash.intersection": "4.4.0",
|
"lodash.intersection": "4.4.0",
|
||||||
|
|
@ -10235,15 +10234,6 @@
|
||||||
"node": ">=4.0"
|
"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": {
|
"node_modules/keyv": {
|
||||||
"version": "4.5.4",
|
"version": "4.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@
|
||||||
"filter-invalid-dom-props": "3.0.1",
|
"filter-invalid-dom-props": "3.0.1",
|
||||||
"hat": "^0.0.3",
|
"hat": "^0.0.3",
|
||||||
"i18next": "^24.0.5",
|
"i18next": "^24.0.5",
|
||||||
"jwt-decode": "^4.0.0",
|
|
||||||
"langs": "github:Stremio/nodejs-langs",
|
"langs": "github:Stremio/nodejs-langs",
|
||||||
"lodash.debounce": "4.0.8",
|
"lodash.debounce": "4.0.8",
|
||||||
"lodash.intersection": "4.4.0",
|
"lodash.intersection": "4.4.0",
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ const Intro = ({ queryParams }) => {
|
||||||
const loginWithApple = React.useCallback(() => {
|
const loginWithApple = React.useCallback(() => {
|
||||||
openLoaderModal();
|
openLoaderModal();
|
||||||
startAppleLogin()
|
startAppleLogin()
|
||||||
.then(({ email, token, sub, name }) => {
|
.then(({ token, sub, email, name }) => {
|
||||||
core.transport.dispatch({
|
core.transport.dispatch({
|
||||||
action: 'Ctx',
|
action: 'Ctx',
|
||||||
args: {
|
args: {
|
||||||
|
|
@ -129,11 +129,7 @@ const Intro = ({ queryParams }) => {
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
closeLoaderModal();
|
closeLoaderModal();
|
||||||
if (error.error === 'popup_closed_by_user') {
|
dispatch({ type: 'error', error: error.message });
|
||||||
dispatch({ type: 'error', error: 'Apple login popup was closed.' });
|
|
||||||
} else {
|
|
||||||
dispatch({ type: 'error', error: error.error });
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
const cancelLoginWithApple = React.useCallback(() => {
|
const cancelLoginWithApple = React.useCallback(() => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
|
// Copyright (C) 2017-2025 Smart code 203358507
|
||||||
|
|
||||||
import { useCallback, useEffect, useRef } from 'react';
|
import { useCallback, useEffect, useRef } from 'react';
|
||||||
import { jwtDecode, JwtPayload } from 'jwt-decode';
|
import { usePlatform } from 'stremio/common';
|
||||||
|
import hat from 'hat';
|
||||||
|
|
||||||
type AppleLoginResponse = {
|
type AppleLoginResponse = {
|
||||||
token: string;
|
token: string;
|
||||||
|
|
@ -8,97 +11,71 @@ type AppleLoginResponse = {
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type AppleSignInResponse = {
|
|
||||||
authorization: {
|
|
||||||
code?: string;
|
|
||||||
id_token: string;
|
|
||||||
state?: string;
|
|
||||||
};
|
|
||||||
email?: string;
|
|
||||||
fullName?: {
|
|
||||||
firstName?: string;
|
|
||||||
lastName?: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type CustomJWTPayload = JwtPayload & {
|
const STREMIO_URL = 'http://localhost:3001';
|
||||||
email?: string;
|
const MAX_TRIES = 25;
|
||||||
};
|
|
||||||
|
|
||||||
const CLIENT_ID = 'com.stremio.services';
|
const getCredentials = async (state: string): Promise<AppleLoginResponse> => {
|
||||||
|
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<AppleLoginResponse>, () => void] => {
|
const useAppleLogin = (): [() => Promise<AppleLoginResponse>, () => void] => {
|
||||||
|
const platform = usePlatform();
|
||||||
const started = useRef(false);
|
const started = useRef(false);
|
||||||
|
const timeout = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
const start = useCallback((): Promise<AppleLoginResponse> => {
|
const start = useCallback(() => new Promise<AppleLoginResponse>((resolve, reject) => {
|
||||||
return new Promise((resolve, reject) => {
|
started.current = true;
|
||||||
if (typeof window.AppleID === 'undefined') {
|
const state = hat(128);
|
||||||
reject(new Error('Apple Sign-In not loaded'));
|
let tries = 0;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
platform.openExternal(`${STREMIO_URL}/login-apple/${state}`);
|
||||||
|
|
||||||
|
const waitForCredentials = () => {
|
||||||
if (started.current) {
|
if (started.current) {
|
||||||
reject(new Error('Apple login already in progress'));
|
timeout.current && clearTimeout(timeout.current);
|
||||||
return;
|
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;
|
waitForCredentials();
|
||||||
|
}), []);
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const stop = useCallback(() => {
|
const stop = useCallback(() => {
|
||||||
started.current = false;
|
started.current = false;
|
||||||
|
timeout.current && clearTimeout(timeout.current);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => stop();
|
return () => stop();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return [start, stop];
|
return [
|
||||||
|
start,
|
||||||
|
stop,
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useAppleLogin;
|
export default useAppleLogin;
|
||||||
|
|
|
||||||
25
src/types/global.d.ts
vendored
25
src/types/global.d.ts
vendored
|
|
@ -26,31 +26,6 @@ interface Chrome {
|
||||||
declare global {
|
declare global {
|
||||||
var qt: Qt | undefined;
|
var qt: Qt | undefined;
|
||||||
var chrome: Chrome | 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 {};
|
export {};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue