refactor: replace authing with oidc-client-ts

This commit is contained in:
linonetwo 2023-12-28 19:08:48 +08:00 committed by lin onetwo
parent 58f469d910
commit 98bc7f1f6c
8 changed files with 78 additions and 164 deletions

View file

@ -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",

82
pnpm-lock.yaml generated
View file

@ -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'}

View file

@ -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<void>, () => Promise<void>] {
const authing = useMemo(
() =>
new AuthenticationClient({
appId: APP_ID,
appHost: APP_DOMAIN,
}),
[],
);
export function useAuth(storageService: SupportedStorageServices, userInfo: IGitUserInfosWithoutToken): [() => Promise<void>, () => Promise<void>] {
const [userManager, setUserManager] = useState<UserManager>();
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: '<YOUR_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];
}

View file

@ -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}`;

View file

@ -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

View file

@ -25,7 +25,7 @@ async function refresh(): Promise<void> {
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',

View file

@ -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';

49
src/type.d.ts vendored
View file

@ -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<ITrackSessionResult>;
logout(): Promise<{ code: number; message?: string }>;
login(): Promise<void>;
}
}
interface IDefaultGatewayInfo {
gateway: string;