feat: try load token upon form open

This commit is contained in:
tiddlygit-test 2021-04-14 01:19:47 +08:00
parent 60bfe7631b
commit df7b661ef3
5 changed files with 105 additions and 72 deletions

View file

@ -10,6 +10,7 @@ import { useUserInfoObservable } from '@services/auth/hooks';
import { usePromiseValueAndSetter } from '@/helpers/useServiceValue';
import { APP_ID, APP_DOMAIN } from '@/constants/auth';
import { ServiceEmailTypes, ServiceTokenTypes, ServiceUserNameTypes } from '@services/auth/interface';
import { useAuthing } from './gitTokenHooks';
const AuthingLoginButton = styled(Button)`
white-space: nowrap;
@ -28,80 +29,12 @@ export function GitTokenForm(props: {
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 authing = useAuthing();
const onFailure = useCallback((error: Error) => {
console.error(error);
}, []);
const onLoginSuccessResponse = useCallback(
async (response: ITrackSessionResult) => {
// DEBUG: console
console.log(`response`, response);
if ('userInfo' in response && response.userInfo?.thirdPartyIdentity?.accessToken !== undefined) {
const accessTokenToSet = response?.userInfo?.thirdPartyIdentity?.accessToken;
const authDataString = response?.userInfo?.oauth;
// all data we need
if (accessTokenToSet !== undefined && authDataString !== undefined) {
const authData = JSON.parse(authDataString);
// DEBUG: console
console.log(`authData`, authData);
const nextUserInfo: IAuthingUserInfo = {
...response.userInfo,
...authData,
...response.userInfo?.thirdPartyIdentity,
};
void window.service.auth.set(`${storageService}-token` as ServiceTokenTypes, accessTokenToSet);
void window.service.auth.set(`${storageService}-userName` as ServiceUserNameTypes, nextUserInfo.username);
void window.service.auth.set(`${storageService}-email` as ServiceEmailTypes, nextUserInfo.email);
if (userName === undefined || (userName === '' && nextUserInfo.username !== userName)) {
userNameSetter(nextUserInfo.username);
}
}
}
},
[storageService, userName, userNameSetter],
);
const onClickLogout = useCallback(async () => {
const { code, message } = await authing.logout();
await window.service.window.clearStorageData();
if (code === 200) {
// TODO: clear the input
} else {
onFailure(new Error(message));
}
}, [authing, onFailure]);
// after authing redirect to 3rd party page and success, it will redirect back, we then check if login is success on component mount
useEffect(() => {
void (async () => {
const response = await authing.trackSession();
// we logout so login into github won't block use from login into gitlab
await onClickLogout();
const isLogin = response?.session !== undefined && response?.session !== null;
if (isLogin) {
await onLoginSuccessResponse(response);
}
})();
}, [authing, onLoginSuccessResponse, onClickLogout]);
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();
@ -114,8 +47,6 @@ export function GitTokenForm(props: {
}, [authing, onFailure]);
const userInfo = useUserInfoObservable();
// DEBUG: console
console.log(`userInfo`, JSON.stringify(userInfo));
if (userInfo === undefined) {
return <div>Loading...</div>;
}

View file

@ -0,0 +1,92 @@
import { useCallback, useEffect, useMemo } from 'react';
import AuthingSSO, { ITrackSessionResult } from '@authing/sso';
import { ServiceTokenTypes, ServiceUserNameTypes, ServiceEmailTypes } from '@services/auth/interface';
import { SupportedStorageServices, IAuthingUserInfo } from '@services/types';
import { APP_ID, APP_DOMAIN } from '@/constants/auth';
import { usePromiseValueAndSetter } from '@/helpers/useServiceValue';
export function useTokenFromAuthingRedirect(authing: AuthingSSO, callback?: () => void): void {
/**
* 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 onLoginSuccessResponse = useCallback(
async (response: ITrackSessionResult) => {
// DEBUG: console
console.log(`response`, response);
if (!('userInfo' in response)) return;
const accessTokenToSet = response?.userInfo?.thirdPartyIdentity?.accessToken;
const provider = response?.userInfo?.thirdPartyIdentity?.provider as SupportedStorageServices;
if (accessTokenToSet !== undefined && provider !== undefined) {
if (!Object.values(SupportedStorageServices).includes(provider)) {
throw new Error(`${provider} not in SupportedStorageServices`);
}
// DEBUG: console
console.log(`provider`, provider);
const authDataString = response?.userInfo?.oauth;
// all data we need
if (accessTokenToSet !== undefined && authDataString !== undefined) {
const authData = JSON.parse(authDataString);
// DEBUG: console
console.log(`authData`, authData);
const nextUserInfo: IAuthingUserInfo = {
...response.userInfo,
...authData,
...response.userInfo?.thirdPartyIdentity,
};
await Promise.all([
window.service.auth.set(`${provider}-token` as ServiceTokenTypes, accessTokenToSet),
window.service.auth.set(`${provider}-userName` as ServiceUserNameTypes, nextUserInfo.username),
window.service.auth.set(`${provider}-email` as ServiceEmailTypes, nextUserInfo.email),
]);
callback();
if (userName === undefined || (userName === '' && nextUserInfo.username !== userName)) {
userNameSetter(nextUserInfo.username);
}
}
}
},
[userName, userNameSetter],
);
const onClickLogout = useCallback(async () => {
const { code, message } = await authing.logout();
await window.service.window.clearStorageData();
if (code === 200) {
// TODO: clear the input
} else {
throw new Error(message);
}
}, [authing]);
// after authing redirect to 3rd party page and success, it will redirect back, we then check if login is success on component mount
useEffect(() => {
void (async () => {
const response = await authing.trackSession();
// we logout so login into github won't block use from login into gitlab
await onClickLogout();
const isLogin = response?.session !== undefined && response?.session !== null;
if (isLogin) {
await onLoginSuccessResponse(response);
}
})();
}, [authing, onLoginSuccessResponse, onClickLogout]);
}
export function useAuthing(): AuthingSSO {
const authing = useMemo(
() =>
new AuthingSSO({
appId: APP_ID,
appDomain: APP_DOMAIN,
redirectUrl: 'http://localhost:3000',
}),
[],
);
return authing;
}

View file

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useCallback } from 'react';
import styled from 'styled-components';
import { useTranslation } from 'react-i18next';
import { AppBar, Paper, Tab } from '@material-ui/core';
@ -16,6 +16,7 @@ import { CloneWikiForm } from './CloneWikiForm';
import { CloneWikiDoneButton } from './CloneWikiDoneButton';
import { useIsCreateMainWorkspace, useIsCreateSyncedWorkspace, useWikiWorkspaceForm } from './useForm';
import { useAuthing, useTokenFromAuthingRedirect } from '@/components/TokenForm/gitTokenHooks';
enum CreateWorkspaceTabs {
CloneOnlineWiki = 'CloneOnlineWiki',
@ -50,6 +51,12 @@ export default function AddWorkspace(): JSX.Element {
const [isCreateSyncedWorkspace, isCreateSyncedWorkspaceSetter] = useIsCreateSyncedWorkspace();
const [isCreateMainWorkspace, isCreateMainWorkspaceSetter] = useIsCreateMainWorkspace();
const authing = useAuthing();
useTokenFromAuthingRedirect(
authing,
useCallback(() => isCreateSyncedWorkspaceSetter(true), []),
);
const form = useWikiWorkspaceForm();
return (

View file

@ -44,6 +44,8 @@ interface IAuthingPostMessageEvent {
window.addEventListener(
'message',
(event: MessageEvent<IAuthingPostMessageEvent>) => {
// DEBUG: console
console.log(`event`, event);
if (typeof event?.data?.code === 'number' && typeof event?.data?.data?.token === 'string' && event?.data.from !== 'preload') {
// This message will be catch by this handler again, so we add a 'from' to indicate that it is re-send by ourself
// we re-send this, so authing in this window can catch it

1
src/type.d.ts vendored
View file

@ -41,6 +41,7 @@ declare module '@authing/sso' {
oauth?: string;
thirdPartyIdentity?: {
accessToken?: string;
provider?: string;
};
}