diff --git a/package.json b/package.json index ad676abe..699ea4c9 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "menubar": "9.3.0", "nanoid": "^4.0.2", "node-fetch": "3.3.2", + "oidc-client-ts": "^2.4.0", "reflect-metadata": "0.1.13", "registry-js": "1.15.1", "rxjs": "7.8.1", @@ -138,7 +139,6 @@ "@types/webpack-node-externals": "3.0.0", "@vercel/webpack-asset-relocator-loader": "1.7.3", "ajv": "^8.12.0", - "authing-js-sdk": "4.23.29", "beautiful-react-hooks": "4.3.0", "chai": "4.3.7", "circular-dependency-plugin": "5.2.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be85a936..7cad2d0b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -110,6 +110,9 @@ dependencies: node-fetch: specifier: 3.3.2 version: 3.3.2 + oidc-client-ts: + specifier: ^2.4.0 + version: 2.4.0 reflect-metadata: specifier: 0.1.13 version: 0.1.13 @@ -318,9 +321,6 @@ devDependencies: ajv: specifier: ^8.12.0 version: 8.12.0 - authing-js-sdk: - specifier: 4.23.29 - version: 4.23.29 beautiful-react-hooks: specifier: 4.3.0 version: 4.3.0(react-dom@18.2.0)(react-router-dom@6.14.1)(react@18.2.0)(rxjs@7.8.1) @@ -4951,19 +4951,6 @@ packages: when-exit: 2.1.0 dev: false - /authing-js-sdk@4.23.29: - resolution: {integrity: sha512-HDoAGqd8M/7CUKuVA8krNICo3rGDpDdVOWnkGhLKL4J1EMMzbotp3ngODP0DW6bE16eWshw2Cxgii+sxIEyP6Q==} - engines: {node: '>=8.9'} - dependencies: - axios: 0.19.2 - crypto-js: 4.1.1 - jsencrypt: 3.3.2 - jwt-decode: 2.2.0 - sm-crypto: 0.3.12 - transitivePeerDependencies: - - supports-color - dev: true - /author-regex@1.0.0: resolution: {integrity: sha512-KbWgR8wOYRAPekEmMXrYYdc7BRyhn2Ftk7KWfMUnQ43hFdojWEFRxhhRUm3/OFEdPa1r0KAvTTg9YQK57xTe0g==} engines: {node: '>=0.8'} @@ -4983,15 +4970,6 @@ packages: fast-glob: 3.3.0 dev: true - /axios@0.19.2: - resolution: {integrity: sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==} - deprecated: Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410 - dependencies: - follow-redirects: 1.5.10 - transitivePeerDependencies: - - supports-color - dev: true - /babel-plugin-polyfill-corejs2@0.4.4(@babel/core@7.22.6): resolution: {integrity: sha512-9WeK9snM1BfxB38goUEv2FLnA6ja07UMfazFHzCXUb3NyDZAwfXvQiURQ6guTTMeHcOsdknULm1PDhs4uWtKyA==} peerDependencies: @@ -5933,9 +5911,9 @@ packages: randomfill: 1.0.4 dev: true - /crypto-js@4.1.1: - resolution: {integrity: sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==} - dev: true + /crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + dev: false /csp-html-webpack-plugin@5.1.0(html-webpack-plugin@5.5.3)(webpack@5.88.1): resolution: {integrity: sha512-6l/s6hACE+UA01PLReNKZfgLZWM98f7ewWmE79maDWIbEXiPcIWQGB3LQR/Zw+hPBj4XPZZ5zNrrO+aygqaLaQ==} @@ -6051,17 +6029,6 @@ packages: dependencies: ms: 2.0.0 - /debug@3.1.0: - resolution: {integrity: sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.0.0 - dev: true - /debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -7794,15 +7761,6 @@ packages: debug: 4.3.4(supports-color@8.1.1) dev: true - /follow-redirects@1.5.10: - resolution: {integrity: sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==} - engines: {node: '>=4.0'} - dependencies: - debug: 3.1.0 - transitivePeerDependencies: - - supports-color - dev: true - /font-awesome@4.7.0: resolution: {integrity: sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=} engines: {node: '>=0.10.3'} @@ -9134,14 +9092,6 @@ packages: argparse: 2.0.1 dev: true - /jsbn@1.1.0: - resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} - dev: true - - /jsencrypt@3.3.2: - resolution: {integrity: sha512-arQR1R1ESGdAxY7ZheWr12wCaF2yF47v5qpB76TtV64H1pyGudk9Hvw8Y9tb/FiTIaaTRUyaSnm5T/Y53Ghm/A==} - dev: true - /jsesc@0.5.0: resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} hasBin: true @@ -9291,9 +9241,9 @@ packages: engines: {node: '>=8'} dev: true - /jwt-decode@2.2.0: - resolution: {integrity: sha512-86GgN2vzfUu7m9Wcj63iUkuDzFNYFVmjeDm2GzWpUk+opB0pEpMsw6ePCMrhYkumz2C1ihqtZzOMAg7FiXcNoQ==} - dev: true + /jwt-decode@3.1.2: + resolution: {integrity: sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==} + dev: false /keycode@2.2.1: resolution: {integrity: sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==} @@ -10357,6 +10307,14 @@ packages: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} dev: true + /oidc-client-ts@2.4.0: + resolution: {integrity: sha512-WijhkTrlXK2VvgGoakWJiBdfIsVGz6CFzgjNNqZU1hPKV2kyeEaJgLs7RwuiSp2WhLfWBQuLvr2SxVlZnk3N1w==} + engines: {node: '>=12.13.0'} + dependencies: + crypto-js: 4.2.0 + jwt-decode: 3.1.2 + dev: false + /omggif@1.0.10: resolution: {integrity: sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==} dev: false @@ -11963,12 +11921,6 @@ packages: is-fullwidth-code-point: 3.0.0 dev: true - /sm-crypto@0.3.12: - resolution: {integrity: sha512-272PBzB4PYaBdeGa41TH9ZlMGLPVRmS36Gs4FjmHwXIdihQypAbhhFWZTaa/3de69q2KfMme1M5O2W5+spAdrg==} - dependencies: - jsbn: 1.1.0 - dev: true - /smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} diff --git a/src/components/TokenForm/gitTokenHooks.ts b/src/components/TokenForm/gitTokenHooks.ts index 096760f2..5c867e64 100644 --- a/src/components/TokenForm/gitTokenHooks.ts +++ b/src/components/TokenForm/gitTokenHooks.ts @@ -1,55 +1,69 @@ /* eslint-disable @typescript-eslint/strict-boolean-expressions */ -import { APP_DOMAIN, APP_ID } from '@/constants/auth'; +import { IGitUserInfosWithoutToken } from '@services/git/interface'; import { SupportedStorageServices } from '@services/types'; -import { AuthenticationClient } from 'authing-js-sdk'; -import { useCallback, useMemo } from 'react'; +import { UserManager, WebStorageStateStore } from 'oidc-client-ts'; +import { useCallback, useEffect, useState } from 'react'; -export function useAuth(storageService: SupportedStorageServices): [() => Promise, () => Promise] { - const authing = useMemo( - () => - new AuthenticationClient({ - appId: APP_ID, - appHost: APP_DOMAIN, - }), - [], - ); +export function useAuth(storageService: SupportedStorageServices, userInfo: IGitUserInfosWithoutToken): [() => Promise, () => Promise] { + const [userManager, setUserManager] = useState(); - const onFailure = useCallback((error: Error) => { - console.error(error); - }, []); - const onClickLogout = useCallback(async () => { - await authing.logout(); - await window.service.window.clearStorageData(); - }, [authing]); + useEffect(() => { + void window.service.context.get('LOGIN_REDIRECT_PATH').then( (LOGIN_REDIRECT_PATH) => { + const settings = { + authority: 'https://github.com/', + client_id: userInfo.gitUserName, + redirect_uri: LOGIN_REDIRECT_PATH, + response_type: 'code', + scope: 'openid', + // post_logout_redirect_uri: '', + userStore: new WebStorageStateStore({ store: window.localStorage }), + }; + + const userManager = new UserManager(settings); + setUserManager(userManager); + + userManager.events.addUserLoaded(async user => { + // Handle the loaded user information here + // Save the access token and other user details + if (user.access_token) { + await window.service.auth.set(`${storageService}-token`, user.access_token); + } + if (userInfo.gitUserName) { + await window.service.auth.set(`${storageService}-userName`, userInfo.gitUserName); + } + if (userInfo.email) { + await window.service.auth.set(`${storageService}-email`, userInfo.email); + } + // Additional user information might be available in the user.profile object + }); + + userManager.events.addSilentRenewError(error => { + console.error('Silent renew error', error); + }); + + // userManager.events.addUserSignedOut(async () => { + // // Handle user sign-out event + // await window.service.window.clearStorageData(); + // }); + }); + }, [storageService, userInfo.email, userInfo.gitUserName]); const onClickLogin = useCallback(async () => { - // clear token first, otherwise github login window won't give us a chance to see the form - // void this.auth.logout(); - // window.remote.clearStorageData(); try { - await authing.social.authorize(storageService, { - onSuccess: async (user) => { - const thirdPartyIdentity = user.identities?.find((identity) => identity?.provider === storageService); - if (thirdPartyIdentity) { - if (thirdPartyIdentity.accessToken) { - await window.service.auth.set(`${storageService}-token`, thirdPartyIdentity.accessToken); - } - if (user.username) { - await window.service.auth.set(`${storageService}-userName`, user.username); - } - if (user.email) { - await window.service.auth.set(`${storageService}-email`, user.email); - } - } - }, - onError: (code, message) => { - onFailure(new Error(message + String(code))); - }, - }); + await userManager?.signinRedirect(); } catch (error) { - onFailure(error as Error); + console.error(error); } - }, [authing.social, onFailure, storageService]); + }, [userManager]); + + const onClickLogout = useCallback(async () => { + try { + await userManager?.signoutRedirect(); + await window.service.window.clearStorageData(); + } catch (error) { + console.error(error); + } + }, [userManager]); return [onClickLogin, onClickLogout]; } diff --git a/src/constants/auth.ts b/src/constants/auth.ts index d4cbe36e..72bf7c7e 100644 --- a/src/constants/auth.ts +++ b/src/constants/auth.ts @@ -1,5 +1,3 @@ -export const APP_ID = '5efdd30e56a87fb76b52809d'; -export const APP_DOMAIN = 'https://tiddlygit-desktop.authing.cn'; export const GITHUB_GRAPHQL_API = 'https://api.github.com/graphql'; export const TIDGI_AUTH_TOKEN_HEADER = 'x-tidgi-auth-token'; export const getTidGiAuthHeaderWithToken = (authToken: string) => `${TIDGI_AUTH_TOKEN_HEADER}-${authToken}`; diff --git a/src/constants/paths.ts b/src/constants/paths.ts index 1c73cff6..4487a2a8 100644 --- a/src/constants/paths.ts +++ b/src/constants/paths.ts @@ -19,7 +19,7 @@ const menuBarIconFileName = isMac ? 'menubarTemplate@2x.png' : 'menubar@2x.png'; export const MENUBAR_ICON_PATH = path.resolve(isDevelopmentOrTest ? buildResourcePath : process.resourcesPath, menuBarIconFileName); export const CHROME_ERROR_PATH = 'chrome-error://chromewebdata/'; -export const LOGIN_REDIRECT_PATH = 'http://localhost:3012/?code='; +export const LOGIN_REDIRECT_PATH = 'http://127.0.0.1:3012/tidgi-github-auth'; export const DESKTOP_PATH = path.join(os.homedir(), 'Desktop'); export const PACKAGE_PATH_BASE = isDevelopmentOrTest diff --git a/src/preload/common/authingPostMessage.ts b/src/preload/common/authRedirect.ts similarity index 90% rename from src/preload/common/authingPostMessage.ts rename to src/preload/common/authRedirect.ts index 9621279d..8b6a5851 100644 --- a/src/preload/common/authingPostMessage.ts +++ b/src/preload/common/authRedirect.ts @@ -25,7 +25,7 @@ async function refresh(): Promise { setTimeout(() => void refresh(), CHECK_LOADED_INTERVAL); return; } - if (window.location.href === CHROME_ERROR_PATH || window.location.href.startsWith(LOGIN_REDIRECT_PATH)) { + if (window.location.href === CHROME_ERROR_PATH || window.location.href.startsWith(`${LOGIN_REDIRECT_PATH}/?code=`)) { await windowService.loadURL(windowName, MAIN_WINDOW_WEBPACK_ENTRY); } else { setTimeout(() => void refresh(), CHECK_LOADED_INTERVAL); @@ -42,7 +42,6 @@ interface IAuthingPostMessageEvent { if (![WindowNames.main, WindowNames.view].includes(windowName)) { setTimeout(() => void refresh(), CHECK_LOADED_INTERVAL); - // Only passing message that Authing needs to the window https://github.com/Authing/Guard/blob/db9df517c00a5eb51e406377ee4d7bb097054b68/src/views/login/SocialButtonsList.vue#L82-L89 // https://stackoverflow.com/questions/55544936/communication-between-preload-and-client-given-context-isolation-in-electron window.addEventListener( 'message', diff --git a/src/preload/index.ts b/src/preload/index.ts index a801a169..92fcf20b 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -12,7 +12,7 @@ import 'electron-ipc-cat/fixContextIsolation'; import { ViewChannel } from '@/constants/channels'; import { IPossibleWindowMeta, WindowNames } from '@services/windows/WindowProperties'; import { browserViewMetaData } from './common/browserViewMetaData'; -import './common/authingPostMessage'; +import './common/authRedirect'; import './view'; import { syncTidgiStateWhenWikiLoads } from './appState'; import { fixAlertConfirm } from './fixer/fixAlertConfirm'; diff --git a/src/type.d.ts b/src/type.d.ts index f1f2c203..1bee1129 100644 --- a/src/type.d.ts +++ b/src/type.d.ts @@ -85,55 +85,6 @@ declare module '*.svg' { const value: string; export default value; } -declare module '@authing/sso' { - export interface ILoginInfo { - urlParams: UrlParameters; - userInfo: UserInfo; - } - export interface ITrackSessionResultSuccess extends ILoginInfo { - session: Session; - } - export interface ITrackSessionResultFailed { - session: null; - } - export type ITrackSessionResult = ITrackSessionResultSuccess | ITrackSessionResultFailed; - - export interface Session { - appId: string; - type: string; - userId: string; - } - - export interface UserInfo { - _id: string; - company: string; - email: string; - nickname: string; - oauth?: string; - photo: string; - registerInClient: string; - thirdPartyIdentity?: { - accessToken?: string; - provider?: string; - }; - token: string; - tokenExpiredAt: string; - username: string; - } - - export interface UrlParameters { - access_token: string; - code: string; - id_token: string; - } - - export default class AuthingSSO { - constructor(options: { appDomain: string; appId: string; redirectUrl: string }); - trackSession(): Promise; - logout(): Promise<{ code: number; message?: string }>; - login(): Promise; - } -} interface IDefaultGatewayInfo { gateway: string;