mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2025-12-06 02:30:47 -08:00
feat: allow user use Authing to get token
This commit is contained in:
parent
af32534288
commit
79847adbb3
5 changed files with 118 additions and 192 deletions
|
|
@ -42,6 +42,7 @@
|
||||||
"ImportWiki": "导入WIKI",
|
"ImportWiki": "导入WIKI",
|
||||||
"CloneWiki": "克隆Wiki",
|
"CloneWiki": "克隆Wiki",
|
||||||
"NotLoggedIn": "未登录",
|
"NotLoggedIn": "未登录",
|
||||||
|
"LogoutToGetStorageServiceToken": "登录在线存储服务以获取凭证",
|
||||||
"LogoutGithubAccount": "登出Github账号",
|
"LogoutGithubAccount": "登出Github账号",
|
||||||
"LoginGithubAccount": "登录Github账号",
|
"LoginGithubAccount": "登录Github账号",
|
||||||
"GitToken": "Git Token",
|
"GitToken": "Git Token",
|
||||||
|
|
|
||||||
111
src/components/TokenForm/GitTokenForm.tsx
Normal file
111
src/components/TokenForm/GitTokenForm.tsx
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
import React, { useMemo, useCallback } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import AuthingSSO from '@authing/sso';
|
||||||
|
|
||||||
|
import { TextField, Button } from '@material-ui/core';
|
||||||
|
|
||||||
|
import { SupportedStorageServices, IAuthingUserInfo } from '@services/types';
|
||||||
|
import { useUserInfoObservable } from '@services/auth/hooks';
|
||||||
|
import { usePromiseValueAndSetter } from '@/helpers/useServiceValue';
|
||||||
|
import GitHubLogin from './AuthingLoginButton';
|
||||||
|
import { APP_ID, APP_DOMAIN } from '@/constants/auth';
|
||||||
|
import { IUserInfos } from '@services/auth/interface';
|
||||||
|
|
||||||
|
const AuthingLoginButton = styled(Button)`
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 100%;
|
||||||
|
`;
|
||||||
|
const GitTokenInput = styled(TextField)``;
|
||||||
|
|
||||||
|
export function GitTokenForm(props: {
|
||||||
|
children?: JSX.Element | Array<JSX.Element | undefined | string>;
|
||||||
|
storageService: SupportedStorageServices;
|
||||||
|
}): JSX.Element {
|
||||||
|
const { children, storageService } = props;
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update tiddlywiki's editor user name when first time creating new workspace
|
||||||
|
*/
|
||||||
|
const [userName, userNameSetter] = usePromiseValueAndSetter(
|
||||||
|
async () => await window.service.auth.get('userName'),
|
||||||
|
async (newUserName) => await window.service.auth.set('userName', newUserName),
|
||||||
|
);
|
||||||
|
|
||||||
|
const authing = useMemo(
|
||||||
|
() =>
|
||||||
|
new AuthingSSO({
|
||||||
|
appId: APP_ID,
|
||||||
|
appDomain: APP_DOMAIN,
|
||||||
|
redirectUrl: 'http://localhost:3000',
|
||||||
|
}),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onFailure = useCallback(async (error: Error) => {}, []);
|
||||||
|
|
||||||
|
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.login();
|
||||||
|
|
||||||
|
const { session, ...response } = await authing.trackSession();
|
||||||
|
const isLogin = session !== null && session !== undefined;
|
||||||
|
if (isLogin && 'userInfo' in response && response.userInfo?.thirdPartyIdentity?.accessToken !== undefined) {
|
||||||
|
const accessTokenToSet = response?.userInfo?.thirdPartyIdentity?.accessToken;
|
||||||
|
const authDataString = response?.userInfo?.oauth;
|
||||||
|
if (accessTokenToSet !== undefined) {
|
||||||
|
void window.service.auth.set((`${storageService}-token` as unknown) as keyof IUserInfos, accessTokenToSet);
|
||||||
|
}
|
||||||
|
// all data we need
|
||||||
|
if (accessTokenToSet !== undefined && authDataString !== undefined) {
|
||||||
|
const authData = JSON.parse(authDataString);
|
||||||
|
const nextUserInfo: IAuthingUserInfo = {
|
||||||
|
...response.userInfo,
|
||||||
|
...authData,
|
||||||
|
...response.userInfo?.thirdPartyIdentity,
|
||||||
|
};
|
||||||
|
delete nextUserInfo.oauth;
|
||||||
|
delete nextUserInfo.thirdPartyIdentity;
|
||||||
|
void window.service.auth.set((`${storageService}-userName` as unknown) as keyof IUserInfos, nextUserInfo.username);
|
||||||
|
if (userName === undefined || userName === '') {
|
||||||
|
userNameSetter(nextUserInfo.username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
void onFailure(error);
|
||||||
|
}
|
||||||
|
}, [authing, onFailure]);
|
||||||
|
const onClickLogout = useCallback(async () => {
|
||||||
|
const { code, message } = await authing.logout();
|
||||||
|
await window.service.window.clearStorageData();
|
||||||
|
if (code === 200) {
|
||||||
|
// TODO: clear the input
|
||||||
|
} else {
|
||||||
|
console.error(message);
|
||||||
|
}
|
||||||
|
}, [authing, onFailure]);
|
||||||
|
|
||||||
|
const userInfo = useUserInfoObservable();
|
||||||
|
if (userInfo === undefined) {
|
||||||
|
return <div>Loading...</div>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AuthingLoginButton>{t('AddWorkspace.LogoutToGetStorageServiceToken')}</AuthingLoginButton>
|
||||||
|
<GitTokenInput
|
||||||
|
helperText={t('AddWorkspace.GitTokenDescription')}
|
||||||
|
fullWidth
|
||||||
|
onChange={(event) => {
|
||||||
|
void window.service.auth.set(`${storageService}-token`, event.target.value);
|
||||||
|
}}
|
||||||
|
value={userInfo[`${storageService}-token`]}
|
||||||
|
/>
|
||||||
|
{children}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,9 @@ import { TabPanel, TabContext, TabList } from '@material-ui/lab';
|
||||||
import { SupportedStorageServices } from '@services/types';
|
import { SupportedStorageServices } from '@services/types';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import GitHubLogin from './AuthingLoginButton';
|
||||||
|
import { GitTokenForm } from './GitTokenForm';
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -47,7 +50,9 @@ export function TokenForm(): JSX.Element {
|
||||||
<Tab label="GitLab" value={SupportedStorageServices.gitlab} />
|
<Tab label="GitLab" value={SupportedStorageServices.gitlab} />
|
||||||
<Tab label="Gitee" value={SupportedStorageServices.gitee} />
|
<Tab label="Gitee" value={SupportedStorageServices.gitee} />
|
||||||
</TabList>
|
</TabList>
|
||||||
<TabPanel value={SupportedStorageServices.github}>Item One</TabPanel>
|
<TabPanel value={SupportedStorageServices.github}>
|
||||||
|
<GitTokenForm storageService={SupportedStorageServices.github} />
|
||||||
|
</TabPanel>
|
||||||
<TabPanel value={SupportedStorageServices.gitlab}>Item Two</TabPanel>
|
<TabPanel value={SupportedStorageServices.gitlab}>Item Two</TabPanel>
|
||||||
<TabPanel value={SupportedStorageServices.gitee}>Item Two</TabPanel>
|
<TabPanel value={SupportedStorageServices.gitee}>Item Two</TabPanel>
|
||||||
</TabsContainer>
|
</TabsContainer>
|
||||||
|
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import TextField from '@material-ui/core/TextField';
|
|
||||||
|
|
||||||
import GitHubLogin from './github-login';
|
|
||||||
import type { IAuthingUserInfo } from '@services/types';
|
|
||||||
import { useUserInfoObservable } from '@services/auth/hooks';
|
|
||||||
|
|
||||||
const GitTokenInput = styled(TextField)``;
|
|
||||||
|
|
||||||
export const setGithubToken = async (token: string | undefined): Promise<void> => await window.service.auth.set('github-token', token);
|
|
||||||
export const getGithubToken = async (): Promise<string | undefined> => await window.service.auth.get('github-token');
|
|
||||||
|
|
||||||
export function GithubTokenForm(props: { children?: JSX.Element | Array<JSX.Element | undefined | string> }): JSX.Element {
|
|
||||||
const { children } = props;
|
|
||||||
const { t } = useTranslation();
|
|
||||||
useEffect(() => {
|
|
||||||
// on startup, loading the cachedGithubToken
|
|
||||||
if (accessToken === undefined && cachedGithubToken !== undefined) {
|
|
||||||
graphqlClient.setHeader('Authorization', `bearer ${cachedGithubToken}`);
|
|
||||||
accessTokenSetter(cachedGithubToken);
|
|
||||||
} else if (accessToken !== undefined && accessToken !== cachedGithubToken) {
|
|
||||||
// if user or login button changed the token, we use latest token
|
|
||||||
Object.keys(graphqlClient.headers).map((key) => graphqlClient.removeHeader(key));
|
|
||||||
accessTokenSetter(accessToken);
|
|
||||||
void setGithubToken(accessToken);
|
|
||||||
}
|
|
||||||
}, [cachedGithubToken, accessToken]);
|
|
||||||
const userInfo = useUserInfoObservable();
|
|
||||||
if (userInfo === undefined) {
|
|
||||||
return <div>Loading...</div>;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<GitHubLogin
|
|
||||||
onSuccess={(response) => {
|
|
||||||
const accessTokenToSet = response?.userInfo?.thirdPartyIdentity?.accessToken;
|
|
||||||
const authDataString = response?.userInfo?.oauth;
|
|
||||||
if (accessTokenToSet !== undefined) {
|
|
||||||
void window.service.auth.set('github-token', accessTokenToSet);
|
|
||||||
}
|
|
||||||
// all data we need
|
|
||||||
if (accessTokenToSet !== undefined && authDataString !== undefined) {
|
|
||||||
const authData = JSON.parse(authDataString);
|
|
||||||
const nextUserInfo = {
|
|
||||||
...response.userInfo,
|
|
||||||
...authData,
|
|
||||||
...response.userInfo?.thirdPartyIdentity,
|
|
||||||
};
|
|
||||||
delete nextUserInfo.oauth;
|
|
||||||
delete nextUserInfo.thirdPartyIdentity;
|
|
||||||
void window.service.auth.set('github-userName', (nextUserInfo as IAuthingUserInfo).username);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onLogout={() => {
|
|
||||||
void window.service.auth.set('github-token', '');
|
|
||||||
void window.service.auth.set('github-userName', '');
|
|
||||||
}}
|
|
||||||
onFailure={() => {
|
|
||||||
void window.service.auth.set('github-token', '');
|
|
||||||
void window.service.auth.set('github-userName', '');
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<GitTokenInput
|
|
||||||
helperText={t('AddWorkspace.GitTokenDescription')}
|
|
||||||
fullWidth
|
|
||||||
onChange={(event) => {
|
|
||||||
void window.service.auth.set('github-token', event.target.value);
|
|
||||||
}}
|
|
||||||
value={userInfo['github-token']}
|
|
||||||
/>
|
|
||||||
{children}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
/* eslint-disable promise/no-nesting */
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import Button from '@material-ui/core/Button';
|
|
||||||
import GithubIcon from '@material-ui/icons/GitHub';
|
|
||||||
import AuthingSSO, { ILoginInfo } from '@authing/sso';
|
|
||||||
import { withTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import { APP_DOMAIN, APP_ID } from '../../constants/auth';
|
|
||||||
|
|
||||||
const SyncToGithubButton = styled(Button)`
|
|
||||||
white-space: nowrap;
|
|
||||||
width: 100%;
|
|
||||||
`;
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
t: (x: string) => string;
|
|
||||||
onRequest: () => unknown;
|
|
||||||
onSuccess: (info: Partial<ILoginInfo>) => unknown;
|
|
||||||
onLogout: () => unknown;
|
|
||||||
onFailure: (error: Error) => unknown;
|
|
||||||
}
|
|
||||||
interface State {
|
|
||||||
isLogin: boolean;
|
|
||||||
}
|
|
||||||
class GitHubLogin extends Component<Props, State> {
|
|
||||||
static defaultProps: Partial<Props> = {
|
|
||||||
onRequest: () => {},
|
|
||||||
onSuccess: () => {},
|
|
||||||
onLogout: () => {},
|
|
||||||
onFailure: () => {},
|
|
||||||
};
|
|
||||||
|
|
||||||
auth: AuthingSSO;
|
|
||||||
intervalHandel?: NodeJS.Timeout;
|
|
||||||
|
|
||||||
constructor(props: Props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
isLogin: false,
|
|
||||||
};
|
|
||||||
this.auth = new AuthingSSO({
|
|
||||||
appId: APP_ID,
|
|
||||||
appDomain: APP_DOMAIN,
|
|
||||||
redirectUrl: 'http://localhost:3000',
|
|
||||||
});
|
|
||||||
this.updateLoginState();
|
|
||||||
}
|
|
||||||
|
|
||||||
async isLogin(): Promise<boolean> {
|
|
||||||
const { onSuccess, onLogout } = this.props;
|
|
||||||
const { session, ...rest } = await this.auth.trackSession();
|
|
||||||
const isLogin = session !== null && session !== undefined;
|
|
||||||
if (isLogin) {
|
|
||||||
onSuccess(rest);
|
|
||||||
} else {
|
|
||||||
onLogout();
|
|
||||||
}
|
|
||||||
return isLogin;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateLoginState(): void {
|
|
||||||
this.intervalHandel = setInterval(() => {
|
|
||||||
void this.isLogin().then((isLogin) => {
|
|
||||||
this.setState({ isLogin });
|
|
||||||
if (this.intervalHandel !== undefined) {
|
|
||||||
clearInterval(this.intervalHandel);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
render(): JSX.Element {
|
|
||||||
const { onRequest, onLogout, onFailure, t } = this.props;
|
|
||||||
const { isLogin } = this.state;
|
|
||||||
return isLogin ? (
|
|
||||||
<SyncToGithubButton
|
|
||||||
onClick={async () => {
|
|
||||||
const { code, message } = await this.auth.logout();
|
|
||||||
await window.service.window.clearStorageData();
|
|
||||||
if (code === 200) {
|
|
||||||
this.setState({ isLogin: false });
|
|
||||||
this.updateLoginState();
|
|
||||||
onLogout();
|
|
||||||
} else {
|
|
||||||
console.error(message);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
color="secondary"
|
|
||||||
endIcon={<GithubIcon />}>
|
|
||||||
{t('AddWorkspace.LogoutGithubAccount')}
|
|
||||||
</SyncToGithubButton>
|
|
||||||
) : (
|
|
||||||
<SyncToGithubButton
|
|
||||||
onClick={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 {
|
|
||||||
onRequest();
|
|
||||||
await this.auth.login();
|
|
||||||
} catch (error) {
|
|
||||||
onFailure(error);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
color="secondary"
|
|
||||||
endIcon={<GithubIcon />}>
|
|
||||||
{t('AddWorkspace.LoginGithubAccount')}
|
|
||||||
</SyncToGithubButton>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withTranslation()(GitHubLogin);
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue