@arcblock/did-connect-react 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +13 -0
- package/README.md +134 -0
- package/lib/Address/index.js +4 -0
- package/lib/Avatar/index.js +4 -0
- package/lib/Button/index.js +17 -0
- package/lib/Connect/assets/locale.js +143 -0
- package/lib/Connect/assets/login-bg.png +0 -0
- package/lib/Connect/assets/login-slogan.js +9 -0
- package/lib/Connect/components/action-button.js +26 -0
- package/lib/Connect/components/app-tips.js +132 -0
- package/lib/Connect/components/auto-height.js +31 -0
- package/lib/Connect/components/back-button.js +24 -0
- package/lib/Connect/components/connect-status.js +263 -0
- package/lib/Connect/components/did-connect-title.js +126 -0
- package/lib/Connect/components/download-tips.js +52 -0
- package/lib/Connect/components/loading.js +26 -0
- package/lib/Connect/components/login-item/connect-choose-list.js +249 -0
- package/lib/Connect/components/login-item/login-method-item.js +129 -0
- package/lib/Connect/components/login-item/mobile-login-item.js +114 -0
- package/lib/Connect/components/login-item/passkey-login-item.js +44 -0
- package/lib/Connect/components/login-item/web-login-item.js +97 -0
- package/lib/Connect/components/mask-overlay.js +34 -0
- package/lib/Connect/components/refresh-overlay.js +57 -0
- package/lib/Connect/components/switch-app.js +70 -0
- package/lib/Connect/contexts/state.js +142 -0
- package/lib/Connect/fullpage.js +5 -0
- package/lib/Connect/hooks/auth-url.js +23 -0
- package/lib/Connect/hooks/method-list.js +46 -0
- package/lib/Connect/hooks/page-show.js +17 -0
- package/lib/Connect/hooks/security.js +27 -0
- package/lib/Connect/hooks/token.js +305 -0
- package/lib/Connect/hooks/use-apps.js +19 -0
- package/lib/Connect/hooks/use-quick-connect.js +97 -0
- package/lib/Connect/index.js +498 -0
- package/lib/Connect/landing-page.js +5 -0
- package/lib/Connect/plugins/email/index.js +62 -0
- package/lib/Connect/plugins/email/list-item.js +28 -0
- package/lib/Connect/plugins/email/placeholder.js +283 -0
- package/lib/Connect/plugins/index.js +4 -0
- package/lib/Connect/use-connect.js +164 -0
- package/lib/Connect/with-blocklet.js +15 -0
- package/lib/Connect/with-bridge-call.js +108 -0
- package/lib/Federated/context.js +61 -0
- package/lib/Federated/index.js +7 -0
- package/lib/Logo/index.js +4 -0
- package/lib/OAuth/context.js +234 -0
- package/lib/OAuth/guest.svg.js +5 -0
- package/lib/OAuth/index.js +7 -0
- package/lib/OAuth/passport-switcher.js +114 -0
- package/lib/Passkey/actions.js +165 -0
- package/lib/Passkey/constants.js +4 -0
- package/lib/Passkey/context.js +266 -0
- package/lib/Passkey/dialog.js +277 -0
- package/lib/Passkey/icon.js +13 -0
- package/lib/Passkey/index.js +9 -0
- package/lib/Service/index.js +62 -0
- package/lib/Session/assets/did-spaces-guide-cover.svg.js +135 -0
- package/lib/Session/assets/did-spaces-guide-icon.svg.js +9 -0
- package/lib/Session/context.js +5 -0
- package/lib/Session/did-spaces-guide.js +136 -0
- package/lib/Session/hooks/use-federated.js +64 -0
- package/lib/Session/hooks/use-mobile.js +8 -0
- package/lib/Session/hooks/use-protected-routes.js +11 -0
- package/lib/Session/hooks/use-session-token.js +169 -0
- package/lib/Session/hooks/use-verify.js +45 -0
- package/lib/Session/index.js +896 -0
- package/lib/Session/libs/constants.js +15 -0
- package/lib/Session/libs/did-spaces.js +10 -0
- package/lib/Session/libs/federated.js +42 -0
- package/lib/Session/libs/index.js +15 -0
- package/lib/Session/libs/locales.js +161 -0
- package/lib/Session/libs/login-mobile.js +55 -0
- package/lib/Session/window-focus-aware.js +17 -0
- package/lib/SessionManager/index.js +4 -0
- package/lib/Storage/engine/cookie.js +21 -0
- package/lib/Storage/engine/local-storage.js +36 -0
- package/lib/Storage/index.js +23 -0
- package/lib/User/index.js +6 -0
- package/lib/User/use-did.js +59 -0
- package/lib/User/wrap-did.js +13 -0
- package/lib/WebWalletSWKeeper/index.js +5 -0
- package/lib/constant.js +22 -0
- package/lib/error.js +8 -0
- package/lib/hooks/use-locale.js +7 -0
- package/lib/index.js +33 -0
- package/lib/locales/en.js +17 -0
- package/lib/locales/index.js +10 -0
- package/lib/locales/zh.js +17 -0
- package/lib/package.json.js +7 -0
- package/lib/types.d.ts +355 -0
- package/lib/utils.js +214 -0
- package/package.json +84 -0
- package/src/Address/index.jsx +2 -0
- package/src/Avatar/index.jsx +2 -0
- package/src/Button/Button.stories.jsx +7 -0
- package/src/Button/index.jsx +21 -0
- package/src/Connect/Connect.stories.jsx +34 -0
- package/src/Connect/assets/locale.js +145 -0
- package/src/Connect/assets/login-bg.png +0 -0
- package/src/Connect/assets/login-slogan.js +7 -0
- package/src/Connect/components/action-button.jsx +22 -0
- package/src/Connect/components/app-tips.jsx +156 -0
- package/src/Connect/components/auto-height.jsx +38 -0
- package/src/Connect/components/back-button.jsx +23 -0
- package/src/Connect/components/connect-status.jsx +259 -0
- package/src/Connect/components/did-connect-title.jsx +106 -0
- package/src/Connect/components/download-tips.jsx +55 -0
- package/src/Connect/components/loading.jsx +25 -0
- package/src/Connect/components/login-item/connect-choose-list.jsx +304 -0
- package/src/Connect/components/login-item/login-method-item.jsx +118 -0
- package/src/Connect/components/login-item/mobile-login-item.jsx +179 -0
- package/src/Connect/components/login-item/passkey-login-item.jsx +52 -0
- package/src/Connect/components/login-item/web-login-item.jsx +149 -0
- package/src/Connect/components/mask-overlay.jsx +32 -0
- package/src/Connect/components/refresh-overlay.jsx +52 -0
- package/src/Connect/components/switch-app.jsx +69 -0
- package/src/Connect/contexts/state.jsx +219 -0
- package/src/Connect/fullpage.jsx +3 -0
- package/src/Connect/hooks/auth-url.js +31 -0
- package/src/Connect/hooks/method-list.js +121 -0
- package/src/Connect/hooks/page-show.js +24 -0
- package/src/Connect/hooks/security.js +40 -0
- package/src/Connect/hooks/token.js +639 -0
- package/src/Connect/hooks/use-apps.js +69 -0
- package/src/Connect/hooks/use-quick-connect.js +130 -0
- package/src/Connect/index.jsx +600 -0
- package/src/Connect/landing-page.jsx +3 -0
- package/src/Connect/plugins/email/index.jsx +82 -0
- package/src/Connect/plugins/email/list-item.jsx +31 -0
- package/src/Connect/plugins/email/placeholder.jsx +365 -0
- package/src/Connect/plugins/index.js +2 -0
- package/src/Connect/use-connect.jsx +321 -0
- package/src/Connect/with-blocklet.jsx +26 -0
- package/src/Connect/with-bridge-call.jsx +138 -0
- package/src/Federated/context.jsx +93 -0
- package/src/Federated/index.jsx +1 -0
- package/src/Logo/index.jsx +2 -0
- package/src/OAuth/context.jsx +346 -0
- package/src/OAuth/guest.svg +20 -0
- package/src/OAuth/index.jsx +1 -0
- package/src/OAuth/passport-switcher.jsx +133 -0
- package/src/Passkey/actions.jsx +212 -0
- package/src/Passkey/constants.js +2 -0
- package/src/Passkey/context.jsx +381 -0
- package/src/Passkey/dialog.jsx +391 -0
- package/src/Passkey/icon.jsx +10 -0
- package/src/Passkey/index.jsx +2 -0
- package/src/Service/index.jsx +96 -0
- package/src/Session/assets/did-spaces-guide-cover.svg +128 -0
- package/src/Session/assets/did-spaces-guide-icon.svg +7 -0
- package/src/Session/context.jsx +7 -0
- package/src/Session/did-spaces-guide.jsx +173 -0
- package/src/Session/hooks/use-federated.js +88 -0
- package/src/Session/hooks/use-mobile.jsx +6 -0
- package/src/Session/hooks/use-protected-routes.js +16 -0
- package/src/Session/hooks/use-session-token.js +365 -0
- package/src/Session/hooks/use-verify.jsx +76 -0
- package/src/Session/index.jsx +1687 -0
- package/src/Session/libs/constants.js +14 -0
- package/src/Session/libs/did-spaces.js +38 -0
- package/src/Session/libs/federated.js +79 -0
- package/src/Session/libs/index.js +5 -0
- package/src/Session/libs/locales.js +160 -0
- package/src/Session/libs/login-mobile.js +80 -0
- package/src/Session/window-focus-aware.jsx +28 -0
- package/src/SessionManager/index.jsx +2 -0
- package/src/Storage/engine/cookie.js +23 -0
- package/src/Storage/engine/local-storage.js +55 -0
- package/src/Storage/index.js +25 -0
- package/src/User/index.js +4 -0
- package/src/User/use-did.js +80 -0
- package/src/User/wrap-did.jsx +18 -0
- package/src/WebWalletSWKeeper/index.jsx +3 -0
- package/src/constant.js +26 -0
- package/src/error.js +6 -0
- package/src/hooks/use-locale.jsx +6 -0
- package/src/index.js +43 -0
- package/src/locales/en.jsx +15 -0
- package/src/locales/index.jsx +13 -0
- package/src/locales/zh.jsx +15 -0
- package/src/types.d.ts +355 -0
- package/src/utils.js +395 -0
- package/vite.config.mjs +29 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* eslint-disable import/prefer-default-export */
|
|
2
|
+
|
|
3
|
+
export const EVENTS = {
|
|
4
|
+
LOGIN: Symbol('login'),
|
|
5
|
+
CANCEL_LOGIN: Symbol('cancelLogin'),
|
|
6
|
+
LOGOUT: Symbol('logout'),
|
|
7
|
+
SWITCH_PROFILE: Symbol('switchProfile'),
|
|
8
|
+
CANCEL_SWITCH_PROFILE: Symbol('cancelSwitchProfile'),
|
|
9
|
+
SWITCH_PASSPORT: Symbol('switchPassport'),
|
|
10
|
+
CANCEL_SWITCH_PASSPORT: Symbol('cancelSwitchPassport'),
|
|
11
|
+
BIND_WALLET: Symbol('bindWallet'),
|
|
12
|
+
CANCEL_BIND_WALLET: Symbol('cancelBindWallet'),
|
|
13
|
+
DID_SPACE_CONNECTED: Symbol('didSpaceConnected'),
|
|
14
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* eslint-disable require-await */
|
|
2
|
+
/* eslint-disable import/prefer-default-export */
|
|
3
|
+
|
|
4
|
+
import { BLOCKLET_SERVICE_PATH_PREFIX, DID_SPACES_BASE_URL } from '../../constant';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @description
|
|
8
|
+
* @export
|
|
9
|
+
* @param {*} user
|
|
10
|
+
* @return {Promise<boolean>}
|
|
11
|
+
*/
|
|
12
|
+
export async function didSpacesIsRequired(user) {
|
|
13
|
+
if (user?.didSpace?.endpoint) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (!window.blocklet) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// 如果是在 iframe 中,直接返回 false
|
|
22
|
+
if (window.self !== window.top) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (window.location.origin === new URL(DID_SPACES_BASE_URL).origin) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// @note: 如果在 blocklet service 页面中,那么默认不展示 did-spaces 引导
|
|
31
|
+
if (window.location.href.includes(BLOCKLET_SERVICE_PATH_PREFIX)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const { componentMountPoints } = window.blocklet;
|
|
36
|
+
const [, componentDid] = window.blocklet.componentId.split('/');
|
|
37
|
+
return componentMountPoints.some((c) => c?.capabilities?.didSpace === 'requiredOnConnect' && componentDid === c.did);
|
|
38
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { joinURL } from 'ufo';
|
|
2
|
+
import { getFederatedEnabled, getMaster } from '@arcblock/ux/lib/Util/federated';
|
|
3
|
+
|
|
4
|
+
import { createAxios, sleep } from '../../utils';
|
|
5
|
+
import { BLOCKLET_SERVICE_PATH_PREFIX } from '../../constant';
|
|
6
|
+
|
|
7
|
+
const api = createAxios();
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 获取 master 的 token
|
|
11
|
+
* @returns {Promise<{sessionToken: string; refreshToken: string;}>}
|
|
12
|
+
*/
|
|
13
|
+
async function getMasterToken() {
|
|
14
|
+
const url = joinURL(BLOCKLET_SERVICE_PATH_PREFIX, '/api/federated/loginMaster');
|
|
15
|
+
const { data } = await api.post(url);
|
|
16
|
+
return data;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// eslint-disable-next-line require-await
|
|
20
|
+
export async function mountFederatedIframe({ mode, refreshToken = '', blocklet }) {
|
|
21
|
+
const master = getMaster(blocklet);
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
if (!['login', 'logout'].includes(mode)) {
|
|
24
|
+
resolve();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const iframe = document.createElement('iframe');
|
|
29
|
+
iframe.style.display = 'none';
|
|
30
|
+
iframe.onload = async () => {
|
|
31
|
+
// HACK: 即使当前页面已经 Load 成功,也强制等待 500ms,确保不会产生边缘情况的问题
|
|
32
|
+
await sleep(500);
|
|
33
|
+
document.body.removeChild(iframe);
|
|
34
|
+
resolve();
|
|
35
|
+
};
|
|
36
|
+
iframe.src = joinURL(
|
|
37
|
+
master.appUrl,
|
|
38
|
+
BLOCKLET_SERVICE_PATH_PREFIX,
|
|
39
|
+
`functional/federated?mode=${mode}&refreshToken=${refreshToken}`
|
|
40
|
+
);
|
|
41
|
+
document.body.appendChild(iframe);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function loginFederatedMaster(params = {}, { fetchToken = true, blocklet } = {}) {
|
|
46
|
+
const enableFederated = getFederatedEnabled();
|
|
47
|
+
if (!enableFederated) {
|
|
48
|
+
console.warn('Federated not enabled');
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* @type {string}
|
|
53
|
+
*/
|
|
54
|
+
let sessionToken;
|
|
55
|
+
/**
|
|
56
|
+
* @type {string}
|
|
57
|
+
*/
|
|
58
|
+
let refreshToken;
|
|
59
|
+
|
|
60
|
+
if (fetchToken) {
|
|
61
|
+
({ sessionToken, refreshToken } = await getMasterToken());
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const master = getMaster(blocklet);
|
|
65
|
+
const url = joinURL(master.appUrl, BLOCKLET_SERVICE_PATH_PREFIX, '/api/federated/login');
|
|
66
|
+
const options = { withCredentials: true };
|
|
67
|
+
|
|
68
|
+
if (sessionToken) {
|
|
69
|
+
options.headers = {
|
|
70
|
+
Authorization: `Bearer ${sessionToken}`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const { data } = await api.post(url, params, options);
|
|
75
|
+
if (refreshToken) {
|
|
76
|
+
await mountFederatedIframe({ mode: 'login', refreshToken, blocklet });
|
|
77
|
+
}
|
|
78
|
+
return data;
|
|
79
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/* eslint-disable import/prefer-default-export */
|
|
2
|
+
export const translations = {
|
|
3
|
+
login: {
|
|
4
|
+
en: {
|
|
5
|
+
title: 'Connect',
|
|
6
|
+
scan: 'Please use the following methods to complete the connection',
|
|
7
|
+
confirm: 'Confirm connection in your DID Wallet',
|
|
8
|
+
success: 'Successfully connected',
|
|
9
|
+
},
|
|
10
|
+
zh: {
|
|
11
|
+
title: '登录',
|
|
12
|
+
scan: '请使用下面的方式完成登录',
|
|
13
|
+
confirm: '在 DID Wallet 中确认连接',
|
|
14
|
+
success: '登录成功',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
'switch-specified-did': {
|
|
18
|
+
en: {
|
|
19
|
+
title: 'Switch Account',
|
|
20
|
+
scan: 'You need switch DID to {did} to complete the following actions',
|
|
21
|
+
confirm: 'Confirm switch in your DID Wallet',
|
|
22
|
+
success: 'Successfully switched',
|
|
23
|
+
},
|
|
24
|
+
zh: {
|
|
25
|
+
title: '切换账号',
|
|
26
|
+
scan: '你需要切换账号至 {did} 才能完成后续的操作',
|
|
27
|
+
confirm: '在 DID Wallet 中确认切换',
|
|
28
|
+
success: '切换成功',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
'switch-did': {
|
|
32
|
+
en: {
|
|
33
|
+
title: 'Add Account',
|
|
34
|
+
scan: 'Please use the following methods to add a new account',
|
|
35
|
+
confirm: 'Confirm switch in your DID Wallet',
|
|
36
|
+
success: 'Successfully added',
|
|
37
|
+
},
|
|
38
|
+
zh: {
|
|
39
|
+
title: '添加账号',
|
|
40
|
+
scan: '请使用下列方式添加新的账号',
|
|
41
|
+
confirm: '在 DID Wallet 中确认切换',
|
|
42
|
+
success: '添加成功',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
'switch-profile': {
|
|
46
|
+
en: {
|
|
47
|
+
title: 'Switch Profile',
|
|
48
|
+
scan: 'Use DID Wallet to switch profile',
|
|
49
|
+
confirm: 'Select profile in your DID Wallet',
|
|
50
|
+
success: 'Profile updated',
|
|
51
|
+
},
|
|
52
|
+
zh: {
|
|
53
|
+
title: '切换个人信息',
|
|
54
|
+
scan: '请使用 DID Wallet 来切换个人信息',
|
|
55
|
+
confirm: '在 DID Wallet 中选择个人信息',
|
|
56
|
+
success: '个人信息已经更新',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
'switch-passport': {
|
|
60
|
+
en: {
|
|
61
|
+
title: 'Switch Passport',
|
|
62
|
+
scan: 'Use DID Wallet to switch passport',
|
|
63
|
+
confirm: 'Select passport in your DID Wallet',
|
|
64
|
+
success: 'Passport and session updated',
|
|
65
|
+
},
|
|
66
|
+
zh: {
|
|
67
|
+
title: '切换通行证',
|
|
68
|
+
scan: '请使用 DID Wallet 来切换通行证',
|
|
69
|
+
confirm: '在 DID Wallet 中选择通行证',
|
|
70
|
+
success: '通行证和会话已经更新',
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
'connect-to-did-spaces-for-user': {
|
|
74
|
+
en: {
|
|
75
|
+
title: 'Please authorize the application to connect to your DID Space',
|
|
76
|
+
scan: 'Please use your DID wallet to complete this authorization',
|
|
77
|
+
confirm: 'Select the DID Spaces NFT in your DID wallet',
|
|
78
|
+
success: 'Connection successful',
|
|
79
|
+
},
|
|
80
|
+
zh: {
|
|
81
|
+
title: '请授权应用连接你的 DID Space',
|
|
82
|
+
scan: '请使用 DID Wallet 来完成授权',
|
|
83
|
+
confirm: '在 DID Wallet 中选择 DID Spaces NFT',
|
|
84
|
+
success: '连接成功',
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
'bind-wallet': {
|
|
88
|
+
en: {
|
|
89
|
+
title: 'Connect DID Wallet for enhanced security',
|
|
90
|
+
scan: 'Use DID Wallet to connect your account',
|
|
91
|
+
success: 'Successfully connected',
|
|
92
|
+
},
|
|
93
|
+
zh: {
|
|
94
|
+
title: '连接 DID Wallet 获得更高的安全性',
|
|
95
|
+
scan: '请使用 DID Wallet 来连接您的账户',
|
|
96
|
+
success: '连接成功',
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
exchangePassport: {
|
|
100
|
+
en: {
|
|
101
|
+
action: 'Exchange Passport with NFT',
|
|
102
|
+
tooltip: 'Exchange passport if you owns any required NFT',
|
|
103
|
+
},
|
|
104
|
+
zh: {
|
|
105
|
+
action: '兑换通行证',
|
|
106
|
+
tooltip: '通过出示你持有的特定 NFT 来获取通行证',
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
lostPassport: {
|
|
110
|
+
en: {
|
|
111
|
+
action: 'Lost Passport',
|
|
112
|
+
},
|
|
113
|
+
zh: {
|
|
114
|
+
action: '找回通行证',
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
switchAccountDialog: {
|
|
119
|
+
en: {
|
|
120
|
+
confirm: 'Switch',
|
|
121
|
+
title: 'Switch Account',
|
|
122
|
+
description: 'Please switch accounts to complete next action',
|
|
123
|
+
currentAccount: 'Current account',
|
|
124
|
+
nextAccount: 'Account to be switched',
|
|
125
|
+
},
|
|
126
|
+
zh: {
|
|
127
|
+
confirm: '切换',
|
|
128
|
+
title: '切换账号',
|
|
129
|
+
description: '请切换账号来完成后续的操作',
|
|
130
|
+
currentAccount: '当前登录的账户',
|
|
131
|
+
nextAccount: '即将切换的账户',
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
verify: {
|
|
135
|
+
zh: {
|
|
136
|
+
title: '验证所有权',
|
|
137
|
+
verify: '验证',
|
|
138
|
+
reconnect: '换个应用',
|
|
139
|
+
scan: '使用以下方法证明所有权',
|
|
140
|
+
confirm: '请在钱包中确认签名,以允许服务器下载并解密备份',
|
|
141
|
+
success: '所有权验证成功,正在进行还原',
|
|
142
|
+
exists: '相同 DID 的应用已存在此节点上,没有继续恢复',
|
|
143
|
+
open: '访问此应用',
|
|
144
|
+
overwrite: '覆盖安装',
|
|
145
|
+
abort: '访问验证已终止',
|
|
146
|
+
},
|
|
147
|
+
en: {
|
|
148
|
+
title: 'Verify Ownership',
|
|
149
|
+
verify: 'Verify',
|
|
150
|
+
reconnect: 'Try another blocklet',
|
|
151
|
+
scan: 'Use the following methods to prove ownership',
|
|
152
|
+
confirm: 'Sign delegation to allow server to download and decrypt the backup',
|
|
153
|
+
success: 'Ownership verified, the restoring is in progress',
|
|
154
|
+
exists: 'This blocklet already exists on this server and restore is aborted',
|
|
155
|
+
open: 'Visit Blocklet',
|
|
156
|
+
overwrite: 'Overwrite',
|
|
157
|
+
abort: 'Access verification aborted',
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { getApps } from '@arcblock/ux/lib/Util/federated';
|
|
2
|
+
import { isUrl } from '@arcblock/ux/lib/Util';
|
|
3
|
+
import { joinURL, withQuery } from 'ufo';
|
|
4
|
+
import semver from 'semver';
|
|
5
|
+
import bridge from '@arcblock/bridge';
|
|
6
|
+
import pathToRegexp from 'path-to-regexp';
|
|
7
|
+
|
|
8
|
+
import { BLOCKLET_SERVICE_PATH_PREFIX } from '../../constant';
|
|
9
|
+
import { logger } from '../../utils';
|
|
10
|
+
|
|
11
|
+
const BLACK_LIST = [
|
|
12
|
+
`${BLOCKLET_SERVICE_PATH_PREFIX}/connect`,
|
|
13
|
+
`${BLOCKLET_SERVICE_PATH_PREFIX}/login`,
|
|
14
|
+
`${BLOCKLET_SERVICE_PATH_PREFIX}/invite`,
|
|
15
|
+
`${BLOCKLET_SERVICE_PATH_PREFIX}/issue-passport`,
|
|
16
|
+
`${BLOCKLET_SERVICE_PATH_PREFIX}/oauth/*`,
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const blackListRoute = BLACK_LIST.map((item) => pathToRegexp(item, []));
|
|
20
|
+
|
|
21
|
+
export async function login() {
|
|
22
|
+
const blocklet = window?.blocklet || {};
|
|
23
|
+
const currentPath = window.location.pathname;
|
|
24
|
+
const { componentId, CHAIN_TYPE: chainType = 'arcblock', appPid } = blocklet;
|
|
25
|
+
const matched = blackListRoute.some((item) => item.exec(currentPath));
|
|
26
|
+
if (matched) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const apps = getApps();
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
// eth 多个地址和统一登录该如何组合?
|
|
34
|
+
// member 的 url
|
|
35
|
+
const searchParams = new URLSearchParams(window.location.search);
|
|
36
|
+
const inviter = searchParams.get('inviter') || localStorage.getItem('inviter');
|
|
37
|
+
const target = withQuery(joinURL(window.location.origin, BLOCKLET_SERVICE_PATH_PREFIX, '/api/user/loginByWallet'), {
|
|
38
|
+
inviter,
|
|
39
|
+
});
|
|
40
|
+
const data = await bridge.asyncCall('arcLogin', {
|
|
41
|
+
target,
|
|
42
|
+
chainType,
|
|
43
|
+
appPid,
|
|
44
|
+
componentId,
|
|
45
|
+
apps: apps.map((x) => {
|
|
46
|
+
let appLogo = x?.appLogo;
|
|
47
|
+
if (!isUrl(appLogo)) {
|
|
48
|
+
appLogo = joinURL(x.appUrl, appLogo);
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
appName: x.appName,
|
|
52
|
+
appPid: x.appPid,
|
|
53
|
+
appUrl: x.appUrl,
|
|
54
|
+
appLogo,
|
|
55
|
+
sourceAppPid: x.sourceAppPid,
|
|
56
|
+
};
|
|
57
|
+
}),
|
|
58
|
+
});
|
|
59
|
+
return data;
|
|
60
|
+
} catch (err) {
|
|
61
|
+
logger.error('Failed login mobile', err);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function getMobileVisitorId() {
|
|
67
|
+
const result = await bridge.asyncCall('arcGetVisitorId');
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function checkEnableAutoLogin({ platform, version } = {}) {
|
|
72
|
+
if (platform === 'ios' && version && semver.gte(version, '4.15.6')) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
if (platform === 'android' && version && semver.gte(version, '4.15.1')) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { useMemoizedFn, useDebounceFn } from 'ahooks';
|
|
4
|
+
|
|
5
|
+
function WindowFocusAware({ callback }) {
|
|
6
|
+
const debounceFn = useDebounceFn(callback);
|
|
7
|
+
const handleVisibilityChange = useMemoizedFn(() => {
|
|
8
|
+
if (!document.hidden) {
|
|
9
|
+
debounceFn.run();
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
window.addEventListener('focus', debounceFn.run);
|
|
14
|
+
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
15
|
+
return () => {
|
|
16
|
+
window.removeEventListener('focus', debounceFn.run);
|
|
17
|
+
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
18
|
+
};
|
|
19
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
20
|
+
}, []);
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
WindowFocusAware.propTypes = {
|
|
25
|
+
callback: PropTypes.func.isRequired,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default WindowFocusAware;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import Cookie from 'js-cookie';
|
|
2
|
+
import { getCookieOptions } from '@arcblock/ux/lib/Util';
|
|
3
|
+
|
|
4
|
+
export default class CookieStorageEngine {
|
|
5
|
+
constructor(storageKey, options) {
|
|
6
|
+
this.storageKey = storageKey;
|
|
7
|
+
this.options = options || {};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
setToken(token) {
|
|
11
|
+
return Cookie.set(this.storageKey, token, {
|
|
12
|
+
...getCookieOptions(this.options),
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
getToken() {
|
|
17
|
+
return Cookie.get(this.storageKey);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
removeToken() {
|
|
21
|
+
return Cookie.remove(this.storageKey, getCookieOptions(this.options));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import jwtDecode from 'jwt-decode';
|
|
2
|
+
|
|
3
|
+
export default class LocalStorageEngine {
|
|
4
|
+
constructor(storageKey) {
|
|
5
|
+
this.storageKey = storageKey;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
setToken(token) {
|
|
9
|
+
if (typeof window === 'undefined' || typeof window.localStorage === 'undefined') {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return localStorage.setItem(this.storageKey, token);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
getToken() {
|
|
17
|
+
if (typeof window === 'undefined' || typeof window.localStorage === 'undefined') {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const data = localStorage.getItem(this.storageKey);
|
|
22
|
+
let value;
|
|
23
|
+
let exp;
|
|
24
|
+
try {
|
|
25
|
+
({ value, exp } = JSON.parse(data));
|
|
26
|
+
// exp undefined 代表该结构不是有效的,所以应该直接返回 data
|
|
27
|
+
if (exp === undefined || value === undefined) {
|
|
28
|
+
// 从 value 中读取 exp
|
|
29
|
+
return data;
|
|
30
|
+
}
|
|
31
|
+
} catch {
|
|
32
|
+
value = data;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const decodeValue = jwtDecode(value);
|
|
37
|
+
exp = decodeValue.exp;
|
|
38
|
+
} catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (new Date().getTime() > exp * 1000) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
removeToken() {
|
|
49
|
+
if (typeof window === 'undefined' || typeof window.localStorage === 'undefined') {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return localStorage.removeItem(this.storageKey);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import CookieEngine from './engine/cookie';
|
|
2
|
+
import LocalStorageEngine from './engine/local-storage';
|
|
3
|
+
|
|
4
|
+
export default function createStorage(storageKey = 'did.auth.token', storageEngine = 'ls', storageOptions = {}) {
|
|
5
|
+
if (!storageKey) {
|
|
6
|
+
throw new Error('storageKey must be specified to create a storage');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let storage = null;
|
|
10
|
+
if (storageEngine === 'ls') {
|
|
11
|
+
storage = new LocalStorageEngine(storageKey);
|
|
12
|
+
} else if (storageEngine === 'cookie') {
|
|
13
|
+
storage = new CookieEngine(storageKey, storageOptions);
|
|
14
|
+
} else {
|
|
15
|
+
throw new Error('storageEngine must be ls or cookie');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
getToken: storage.getToken.bind(storage),
|
|
20
|
+
setToken: storage.setToken.bind(storage),
|
|
21
|
+
removeToken: storage.removeToken.bind(storage),
|
|
22
|
+
engine: storageEngine,
|
|
23
|
+
key: storageKey,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
// following utils duplicate from: arcblock/blocklet-server/blocklet/meta/src/did
|
|
4
|
+
export function getPermanentDid(user) {
|
|
5
|
+
return user?.did;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function getConnectedAccounts(user) {
|
|
9
|
+
return user?.connectedAccounts || user?.extraConfigs?.connectedAccounts || [];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getSourceProvider(user) {
|
|
13
|
+
return user?.sourceProvider || user?.extraConfigs?.sourceProvider || 'wallet';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getSourceProviders(user) {
|
|
17
|
+
const connectedAccounts = getConnectedAccounts(user);
|
|
18
|
+
return connectedAccounts.map((item) => item.provider);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function getConnectedDids(user) {
|
|
22
|
+
const connectedAccounts = getConnectedAccounts(user);
|
|
23
|
+
const didList = connectedAccounts.map((item) => item.did);
|
|
24
|
+
return didList;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function getWallet(user) {
|
|
28
|
+
const connectedAccounts = getConnectedAccounts(user);
|
|
29
|
+
const walletAccount = connectedAccounts.find((item) => item.provider === 'wallet');
|
|
30
|
+
return walletAccount;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* @description
|
|
34
|
+
* @export
|
|
35
|
+
* @param {*} user
|
|
36
|
+
* @return {string}
|
|
37
|
+
*/
|
|
38
|
+
export function getWalletDid(user) {
|
|
39
|
+
const walletAccount = getWallet(user);
|
|
40
|
+
return walletAccount?.did;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function useDid({ session }) {
|
|
44
|
+
const [state, setState] = useState({
|
|
45
|
+
did: '',
|
|
46
|
+
wallet: '',
|
|
47
|
+
walletDid: '',
|
|
48
|
+
permanentDid: '',
|
|
49
|
+
connectedDids: [],
|
|
50
|
+
connectedAccounts: [],
|
|
51
|
+
sourceProvider: '',
|
|
52
|
+
sourceProviders: [],
|
|
53
|
+
});
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (session?.user) {
|
|
56
|
+
const permanentDid = getPermanentDid(session.user);
|
|
57
|
+
const walletDid = getWalletDid(session.user);
|
|
58
|
+
const connectedAccounts = getConnectedAccounts(session.user);
|
|
59
|
+
const connectedDids = getConnectedDids(session.user);
|
|
60
|
+
const wallet = getWallet(session.user);
|
|
61
|
+
const sourceProvider = getSourceProvider(session.user);
|
|
62
|
+
const sourceProviders = getSourceProviders(session.user);
|
|
63
|
+
|
|
64
|
+
setState({
|
|
65
|
+
did: permanentDid,
|
|
66
|
+
wallet,
|
|
67
|
+
walletDid,
|
|
68
|
+
permanentDid,
|
|
69
|
+
connectedDids,
|
|
70
|
+
connectedAccounts,
|
|
71
|
+
sourceProvider,
|
|
72
|
+
sourceProviders,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}, [session?.user]);
|
|
76
|
+
|
|
77
|
+
return state;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export default useDid;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
import useDid from './use-did';
|
|
3
|
+
|
|
4
|
+
function WrapDid({ session, children }) {
|
|
5
|
+
const state = useDid({ session });
|
|
6
|
+
|
|
7
|
+
if (children instanceof Function) {
|
|
8
|
+
return children(state);
|
|
9
|
+
}
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
WrapDid.propTypes = {
|
|
14
|
+
session: PropTypes.object.isRequired,
|
|
15
|
+
children: PropTypes.func.isRequired,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default WrapDid;
|
package/src/constant.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const DEFAULT_TIMEOUT = 5 * 60;
|
|
2
|
+
export const DEFAULT_WINDOW_TIMEOUT = 30 * 60;
|
|
3
|
+
export const BLOCKLET_SERVICE_PATH_PREFIX = '/.well-known/service';
|
|
4
|
+
export const SESSION_TOKEN_STORAGE_KEY = 'login_token';
|
|
5
|
+
export const REFRESH_TOKEN_STORAGE_KEY = 'refresh_token';
|
|
6
|
+
|
|
7
|
+
export const RELAY_SOCKET_PREFIX = '/.well-known/service';
|
|
8
|
+
export const API_DID_PREFIX = '/api/did';
|
|
9
|
+
|
|
10
|
+
export const DEFAULT_NAME = 'DID Connect';
|
|
11
|
+
|
|
12
|
+
export const DID_CONNECT_URL_PARAMS_NAME = '__did-connect__';
|
|
13
|
+
|
|
14
|
+
export const BUSY_STATUS = ['scanned', 'succeed', 'error', 'busy'];
|
|
15
|
+
export const EXT_DOWNLOAD_URL = {
|
|
16
|
+
chrome: 'https://chromewebstore.google.com/detail/did-wallet/ibjflpbmadchofnbpppegdbnifdgincp',
|
|
17
|
+
edge: 'https://microsoftedge.microsoft.com/addons/detail/did-wallet/hjgoblidjnnnamdkinbichnfbmghmafd',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const CHECK_STATUS_INTERVAL = 2 * 1000;
|
|
21
|
+
export const VERIFY_CODE_LENGTH = 6;
|
|
22
|
+
|
|
23
|
+
export const LANG_COOKIE_NAME = 'nf_lang';
|
|
24
|
+
export const CHECK_INTERVAL_TIME = 1000;
|
|
25
|
+
|
|
26
|
+
export const DID_SPACES_BASE_URL = window?.blocklet?.DID_SPACES_BASE_URL || 'https://www.didspaces.com/app';
|
package/src/error.js
ADDED