@abtnode/auth 1.15.17 → 1.16.0-beta-b16cb035
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/lib/auth.js +416 -53
- package/lib/invitation.js +98 -0
- package/lib/lost-passport.js +76 -31
- package/lib/passport.js +31 -33
- package/lib/server.js +723 -0
- package/lib/util/create-passport-svg.js +101 -0
- package/lib/util/get-auth-method.js +25 -0
- package/lib/util/passport-color.js +147 -0
- package/package.json +20 -16
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const omit = require('lodash/omit');
|
|
3
|
+
const joinUrl = require('url-join');
|
|
4
|
+
const { WELLKNOWN_SERVICE_PATH_PREFIX, NODE_DATA_DIR_NAME } = require('@abtnode/constant');
|
|
5
|
+
const { BLOCKLET_CONFIGURABLE_KEY } = require('@blocklet/constant');
|
|
6
|
+
const { getDisplayName } = require('@blocklet/meta/lib/util');
|
|
7
|
+
const logger = require('@abtnode/logger')(require('../package.json').name);
|
|
8
|
+
const { parseUserAvatar } = require('@abtnode/util/lib/user-avatar');
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
init(server, node, { prefix, type } = {}) {
|
|
12
|
+
server.get(`${prefix}/invitation`, async (req, res) => {
|
|
13
|
+
const { inviteId } = req.query;
|
|
14
|
+
const groupPathPrefix = req.headers['x-group-path-prefix'] || '/';
|
|
15
|
+
|
|
16
|
+
const nodeInfo = await node.getNodeInfo();
|
|
17
|
+
try {
|
|
18
|
+
let info;
|
|
19
|
+
if (type === 'blocklet') {
|
|
20
|
+
const blockletInfo = await req.getBlocklet();
|
|
21
|
+
info = {
|
|
22
|
+
did: blockletInfo.meta.did,
|
|
23
|
+
appDid: blockletInfo.appDid,
|
|
24
|
+
url: blockletInfo.environmentObj[BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_URL],
|
|
25
|
+
name: getDisplayName(blockletInfo),
|
|
26
|
+
version: blockletInfo.meta.version,
|
|
27
|
+
logo: joinUrl(groupPathPrefix, WELLKNOWN_SERVICE_PATH_PREFIX, '/blocklet/logo'),
|
|
28
|
+
// TODO: 需要将 CHAIN_HOST 纳入标准
|
|
29
|
+
chainHost: blockletInfo.configObj.CHAIN_HOST,
|
|
30
|
+
passportColor: blockletInfo.configObj[BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_PASSPORT_COLOR],
|
|
31
|
+
description: blockletInfo.meta.description,
|
|
32
|
+
dataDir: blockletInfo.env.dataDir,
|
|
33
|
+
};
|
|
34
|
+
} else {
|
|
35
|
+
info = {
|
|
36
|
+
did: nodeInfo.did,
|
|
37
|
+
appDid: nodeInfo.did,
|
|
38
|
+
url: '/',
|
|
39
|
+
name: nodeInfo.name,
|
|
40
|
+
version: nodeInfo.version,
|
|
41
|
+
description: nodeInfo.description,
|
|
42
|
+
dataDir: path.join(node.dataDirs.data, NODE_DATA_DIR_NAME),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const teamDid = info.did;
|
|
47
|
+
const invitations = await node.getInvitations({ teamDid, filter: () => true });
|
|
48
|
+
const invitation = invitations.find((v) => v.inviteId === inviteId);
|
|
49
|
+
if (!invitation || Date.now() > new Date(invitation.expireDate).getTime()) {
|
|
50
|
+
res.status(404).send('Invitation not found or invitation has been used');
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const roles = await node.getRoles({ teamDid });
|
|
55
|
+
const role = roles.find((v) => v.name === invitation.role);
|
|
56
|
+
try {
|
|
57
|
+
role.permissions = await node.getPermissionsByRole({
|
|
58
|
+
teamDid,
|
|
59
|
+
role: { name: role.name },
|
|
60
|
+
});
|
|
61
|
+
} catch (err) {
|
|
62
|
+
logger.error('failed to get role permission', { teamDid, role: role.name, error: err });
|
|
63
|
+
role.permissions = [];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let user = await node.getUser({ teamDid: info.did, user: { did: invitation.inviter.did } });
|
|
67
|
+
let avatar = user && (await parseUserAvatar(user.avatar, { dataDir: info.dataDir }));
|
|
68
|
+
|
|
69
|
+
// blocklet 邀请链接可能是 server 的 member
|
|
70
|
+
if (!user && type === 'blocklet') {
|
|
71
|
+
user = await node.getUser({ teamDid: nodeInfo.did, user: { did: invitation.inviter.did } });
|
|
72
|
+
avatar =
|
|
73
|
+
user &&
|
|
74
|
+
(await parseUserAvatar(user.avatar, { dataDir: path.join(node.dataDirs.data, NODE_DATA_DIR_NAME) }));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const inviter = {
|
|
78
|
+
did: invitation.inviter.did,
|
|
79
|
+
email: invitation.inviter.email,
|
|
80
|
+
fullName: invitation.inviter.fullName || user?.fullName,
|
|
81
|
+
role: invitation.inviter.role,
|
|
82
|
+
avatar,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
res.json({
|
|
86
|
+
...invitation,
|
|
87
|
+
info: omit(info, 'dataDir'),
|
|
88
|
+
inviter,
|
|
89
|
+
inviterRaw: invitation.inviter,
|
|
90
|
+
role: role || {},
|
|
91
|
+
});
|
|
92
|
+
} catch (err) {
|
|
93
|
+
logger.error('failed to get invitation info', { inviteId, error: err });
|
|
94
|
+
res.status(500).end();
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
};
|
package/lib/lost-passport.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
|
+
const path = require('path');
|
|
1
2
|
const joinUrl = require('url-join');
|
|
2
3
|
const uniqBy = require('lodash/uniqBy');
|
|
4
|
+
const uniq = require('lodash/uniq');
|
|
3
5
|
const getBlockletInfo = require('@blocklet/meta/lib/info');
|
|
6
|
+
const formatContext = require('@abtnode/util/lib/format-context');
|
|
4
7
|
const getRandomMessage = require('@abtnode/util/lib/get-random-message');
|
|
5
|
-
const getNodeWallet = require('@abtnode/util/lib/get-
|
|
6
|
-
const {
|
|
8
|
+
const getNodeWallet = require('@abtnode/util/lib/get-app-wallet');
|
|
9
|
+
const { getDisplayName, getBlockletAppIdList } = require('@blocklet/meta/lib/util');
|
|
10
|
+
const { VC_TYPE_NODE_PASSPORT, PASSPORT_STATUS, NODE_DATA_DIR_NAME } = require('@abtnode/constant');
|
|
7
11
|
const get = require('lodash/get');
|
|
12
|
+
const { parseUserAvatar } = require('@abtnode/util/lib/user-avatar');
|
|
8
13
|
|
|
9
14
|
const logger = require('./logger');
|
|
10
15
|
const { messages, getUser, checkWalletVersion, getPassportStatusEndpoint } = require('./auth');
|
|
11
16
|
const {
|
|
12
|
-
createPassportSvg,
|
|
13
17
|
createPassport,
|
|
14
18
|
createPassportVC,
|
|
15
19
|
upsertToPassports,
|
|
@@ -18,30 +22,41 @@ const {
|
|
|
18
22
|
} = require('./passport');
|
|
19
23
|
const verifySignature = require('./util/verify-signature');
|
|
20
24
|
|
|
25
|
+
const createPassportSvg = require('./util/create-passport-svg');
|
|
26
|
+
|
|
21
27
|
const TEAM_TYPES = {
|
|
22
28
|
BLOCKLET: 'blocklet',
|
|
23
29
|
NODE: 'node',
|
|
24
30
|
};
|
|
25
31
|
|
|
26
|
-
const
|
|
32
|
+
const getApplicationInfo = async ({ type, node, req }) => {
|
|
27
33
|
let teamDid;
|
|
28
34
|
let issuerDid;
|
|
35
|
+
let issuerDidList;
|
|
29
36
|
let issuerName;
|
|
30
37
|
let issuerWallet;
|
|
38
|
+
let passportColor;
|
|
39
|
+
let dataDir;
|
|
31
40
|
|
|
32
41
|
const info = await node.getNodeInfo();
|
|
33
42
|
if (type === TEAM_TYPES.NODE) {
|
|
34
43
|
teamDid = info.did;
|
|
35
44
|
issuerDid = info.did;
|
|
45
|
+
issuerDidList = [info.did];
|
|
36
46
|
issuerName = info.name;
|
|
37
47
|
issuerWallet = getNodeWallet(info.sk);
|
|
48
|
+
passportColor = 'default';
|
|
49
|
+
dataDir = path.join(node.dataDirs.data, NODE_DATA_DIR_NAME);
|
|
38
50
|
} else if (TEAM_TYPES.BLOCKLET) {
|
|
39
51
|
teamDid = req.headers['x-blocklet-did'];
|
|
40
52
|
const blocklet = await node.getBlocklet({ did: teamDid, attachRuntimeInfo: false });
|
|
41
|
-
const
|
|
42
|
-
issuerDid = wallet.address;
|
|
43
|
-
|
|
44
|
-
|
|
53
|
+
const blockletInfo = getBlockletInfo(blocklet, info.sk);
|
|
54
|
+
issuerDid = blockletInfo.wallet.address;
|
|
55
|
+
issuerDidList = uniq([blockletInfo.wallet.address, ...getBlockletAppIdList(blocklet)]);
|
|
56
|
+
issuerName = getDisplayName(blocklet, true);
|
|
57
|
+
issuerWallet = blockletInfo.wallet;
|
|
58
|
+
passportColor = blockletInfo.passportColor;
|
|
59
|
+
dataDir = blocklet.env.dataDir;
|
|
45
60
|
} else {
|
|
46
61
|
throw new Error('createLostPassportListRoute: unknown type');
|
|
47
62
|
}
|
|
@@ -49,8 +64,11 @@ const getTeamInfo = async ({ type, node, req }) => {
|
|
|
49
64
|
return {
|
|
50
65
|
teamDid,
|
|
51
66
|
issuerDid,
|
|
67
|
+
issuerDidList,
|
|
52
68
|
issuerName,
|
|
53
69
|
issuerWallet,
|
|
70
|
+
passportColor,
|
|
71
|
+
dataDir,
|
|
54
72
|
};
|
|
55
73
|
};
|
|
56
74
|
|
|
@@ -79,10 +97,10 @@ const createLostPassportListRoute = ({ node, type }) => ({
|
|
|
79
97
|
},
|
|
80
98
|
},
|
|
81
99
|
|
|
82
|
-
onAuth: async ({ userDid, extraParams,
|
|
100
|
+
onAuth: async ({ userDid, extraParams, updateSession, req }) => {
|
|
83
101
|
const { locale } = extraParams;
|
|
84
102
|
|
|
85
|
-
const { teamDid,
|
|
103
|
+
const { teamDid, issuerDidList, dataDir } = await getApplicationInfo({ node, req, type });
|
|
86
104
|
|
|
87
105
|
// check user approved
|
|
88
106
|
const user = await getUser(node, teamDid, userDid);
|
|
@@ -101,14 +119,10 @@ const createLostPassportListRoute = ({ node, type }) => ({
|
|
|
101
119
|
return false;
|
|
102
120
|
}
|
|
103
121
|
|
|
104
|
-
if (x.issuer.id
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
-
if (x.expirationDate && Date.now() > new Date(x.expirationDate).getTime()) {
|
|
122
|
+
if (!issuerDidList.includes(x.issuer.id)) {
|
|
108
123
|
return false;
|
|
109
124
|
}
|
|
110
|
-
|
|
111
|
-
return true;
|
|
125
|
+
return !(x.expirationDate && Date.now() > new Date(x.expirationDate).getTime());
|
|
112
126
|
}),
|
|
113
127
|
'name'
|
|
114
128
|
);
|
|
@@ -119,9 +133,9 @@ const createLostPassportListRoute = ({ node, type }) => ({
|
|
|
119
133
|
|
|
120
134
|
logger.info('get passport type list', { userDid });
|
|
121
135
|
|
|
122
|
-
await
|
|
123
|
-
|
|
124
|
-
});
|
|
136
|
+
user.avatar = await parseUserAvatar(user.avatar, { did: teamDid, dataDir });
|
|
137
|
+
|
|
138
|
+
await updateSession({ user });
|
|
125
139
|
},
|
|
126
140
|
});
|
|
127
141
|
|
|
@@ -132,7 +146,6 @@ const createLostPassportListRoute = ({ node, type }) => ({
|
|
|
132
146
|
const createLostPassportIssueRoute = ({ node, type, authServicePrefix }) => ({
|
|
133
147
|
action: 'lost-passport-issue',
|
|
134
148
|
authPrincipal: false,
|
|
135
|
-
|
|
136
149
|
claims: [
|
|
137
150
|
{
|
|
138
151
|
authPrincipal: async ({ extraParams }) => {
|
|
@@ -144,11 +157,16 @@ const createLostPassportIssueRoute = ({ node, type, authServicePrefix }) => ({
|
|
|
144
157
|
},
|
|
145
158
|
},
|
|
146
159
|
{
|
|
147
|
-
signature: async ({ extraParams, context: { request,
|
|
148
|
-
const { locale, passportName } = extraParams;
|
|
149
|
-
checkWalletVersion({
|
|
160
|
+
signature: async ({ extraParams, context: { request, didwallet } }) => {
|
|
161
|
+
const { locale, passportName, receiverDid } = extraParams;
|
|
162
|
+
checkWalletVersion({ didwallet, locale });
|
|
150
163
|
|
|
151
|
-
const { teamDid, issuerDid, issuerName } = await
|
|
164
|
+
const { teamDid, issuerDid, issuerName, passportColor, dataDir } = await getApplicationInfo({
|
|
165
|
+
node,
|
|
166
|
+
req: request,
|
|
167
|
+
type,
|
|
168
|
+
});
|
|
169
|
+
const user = await getUser(node, teamDid, receiverDid);
|
|
152
170
|
|
|
153
171
|
const passport = await createPassport({
|
|
154
172
|
name: passportName,
|
|
@@ -163,17 +181,28 @@ const createLostPassportIssueRoute = ({ node, type, authServicePrefix }) => ({
|
|
|
163
181
|
type: 'mime:text/plain',
|
|
164
182
|
display: JSON.stringify({
|
|
165
183
|
type: 'svg',
|
|
166
|
-
content: createPassportSvg({
|
|
184
|
+
content: createPassportSvg({
|
|
185
|
+
issuer: issuerName,
|
|
186
|
+
title: passport.title,
|
|
187
|
+
issuerDid,
|
|
188
|
+
ownerName: user.fullName || '',
|
|
189
|
+
ownerAvatarUrl: await parseUserAvatar(user.avatar || '', { dataDir }),
|
|
190
|
+
preferredColor: passportColor,
|
|
191
|
+
}),
|
|
167
192
|
}),
|
|
168
193
|
};
|
|
169
194
|
},
|
|
170
195
|
},
|
|
171
196
|
],
|
|
172
197
|
|
|
173
|
-
onAuth: async ({ claims, userDid, userPk, extraParams, baseUrl, req }) => {
|
|
198
|
+
onAuth: async ({ claims, userDid, userPk, extraParams, updateSession, baseUrl, req }) => {
|
|
174
199
|
const { locale = 'en', receiverDid, passportName } = extraParams;
|
|
175
200
|
|
|
176
|
-
const { teamDid,
|
|
201
|
+
const { teamDid, issuerDidList, issuerName, issuerWallet, passportColor, dataDir } = await getApplicationInfo({
|
|
202
|
+
node,
|
|
203
|
+
req,
|
|
204
|
+
type,
|
|
205
|
+
});
|
|
177
206
|
const statusEndpointBaseUrl = getStatusEndpointBaseUrl(type, baseUrl, authServicePrefix);
|
|
178
207
|
|
|
179
208
|
// Verify signature
|
|
@@ -202,7 +231,7 @@ const createLostPassportIssueRoute = ({ node, type, authServicePrefix }) => ({
|
|
|
202
231
|
(x) =>
|
|
203
232
|
x.name === passportName &&
|
|
204
233
|
x.status === PASSPORT_STATUS.VALID &&
|
|
205
|
-
x.issuer.id
|
|
234
|
+
issuerDidList.includes(x.issuer.id) &&
|
|
206
235
|
(!x.expirationDate || Date.now() > new Date(x.expirationDate).getTime())
|
|
207
236
|
);
|
|
208
237
|
if (!exist) {
|
|
@@ -215,6 +244,8 @@ const createLostPassportIssueRoute = ({ node, type, authServicePrefix }) => ({
|
|
|
215
244
|
);
|
|
216
245
|
}
|
|
217
246
|
|
|
247
|
+
user.avatar = await parseUserAvatar(user.avatar, { dataDir });
|
|
248
|
+
|
|
218
249
|
const vcParams = {
|
|
219
250
|
issuerName,
|
|
220
251
|
issuerWallet,
|
|
@@ -232,25 +263,39 @@ const createLostPassportIssueRoute = ({ node, type, authServicePrefix }) => ({
|
|
|
232
263
|
teamDid,
|
|
233
264
|
}),
|
|
234
265
|
types: [],
|
|
266
|
+
ownerProfile: user,
|
|
267
|
+
preferredColor: passportColor,
|
|
235
268
|
};
|
|
236
269
|
|
|
237
270
|
if (type === TEAM_TYPES.NODE) {
|
|
238
|
-
vcParams.types = [
|
|
271
|
+
vcParams.types = [VC_TYPE_NODE_PASSPORT];
|
|
239
272
|
vcParams.tag = teamDid;
|
|
240
273
|
}
|
|
241
274
|
|
|
242
275
|
const vc = createPassportVC(vcParams);
|
|
243
276
|
|
|
244
277
|
const role = getRoleFromLocalPassport(get(vc, 'credentialSubject.passport'));
|
|
278
|
+
const passport = createUserPassport(vc, { role });
|
|
245
279
|
|
|
246
|
-
await node.updateUser({
|
|
280
|
+
const result = await node.updateUser({
|
|
247
281
|
teamDid,
|
|
248
282
|
user: {
|
|
249
283
|
did: userDid,
|
|
250
284
|
pk: userPk,
|
|
251
|
-
passports: upsertToPassports(user.passports || [],
|
|
285
|
+
passports: upsertToPassports(user.passports || [], passport),
|
|
252
286
|
},
|
|
253
287
|
});
|
|
288
|
+
await node.createAuditLog(
|
|
289
|
+
{
|
|
290
|
+
action: 'updateUser',
|
|
291
|
+
args: { teamDid, userDid, passport, reason: 'recovered passport' },
|
|
292
|
+
context: formatContext(Object.assign(req, { user })),
|
|
293
|
+
result,
|
|
294
|
+
},
|
|
295
|
+
node
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
await updateSession({ passportId: vc.id });
|
|
254
299
|
|
|
255
300
|
return {
|
|
256
301
|
disposition: 'attachment',
|
package/lib/passport.js
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
const Joi = require('joi');
|
|
4
4
|
const pick = require('lodash/pick');
|
|
5
5
|
const { create: createVC } = require('@arcblock/vc');
|
|
6
|
-
const { ROLES,
|
|
6
|
+
const { ROLES, VC_TYPE_GENERAL_PASSPORT, PASSPORT_STATUS } = require('@abtnode/constant');
|
|
7
|
+
const createPassportSvg = require('./util/create-passport-svg');
|
|
7
8
|
|
|
8
9
|
const SPEC_VERSION = '1.0.0';
|
|
9
10
|
|
|
@@ -11,7 +12,7 @@ const passportSchema = Joi.object({
|
|
|
11
12
|
name: Joi.string().required(),
|
|
12
13
|
title: Joi.string(),
|
|
13
14
|
specVersion: Joi.string(),
|
|
14
|
-
endpoint: Joi.string().uri(),
|
|
15
|
+
endpoint: Joi.string().uri(), // deprecated
|
|
15
16
|
});
|
|
16
17
|
|
|
17
18
|
const validatePassport = (d) => {
|
|
@@ -22,17 +23,21 @@ const validatePassport = (d) => {
|
|
|
22
23
|
return value;
|
|
23
24
|
};
|
|
24
25
|
|
|
25
|
-
const createPassport = async ({ name, node, locale = 'en', teamDid, endpoint } = {}) => {
|
|
26
|
+
const createPassport = async ({ name, node, locale = 'en', teamDid, endpoint, role: inputRole } = {}) => {
|
|
26
27
|
const passportNotFound = {
|
|
27
28
|
en: (x) => `The passport was not found: ${x}`,
|
|
28
29
|
zh: (x) => `未找到通行证: ${x}`,
|
|
29
30
|
};
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
const role = roles.find((x) => x.name === name);
|
|
32
|
+
let role = inputRole;
|
|
33
33
|
if (!role) {
|
|
34
|
-
|
|
34
|
+
const roles = await node.getRoles({ teamDid });
|
|
35
|
+
role = roles.find((x) => x.name === name);
|
|
36
|
+
if (!role) {
|
|
37
|
+
throw new Error(passportNotFound[locale](name));
|
|
38
|
+
}
|
|
35
39
|
}
|
|
40
|
+
|
|
36
41
|
const passport = {
|
|
37
42
|
specVersion: SPEC_VERSION,
|
|
38
43
|
name: role.name,
|
|
@@ -48,35 +53,21 @@ const createPassport = async ({ name, node, locale = 'en', teamDid, endpoint } =
|
|
|
48
53
|
return passport;
|
|
49
54
|
};
|
|
50
55
|
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<path d="M234 24.25H289V23.75H234V24.25ZM292.75 28V47H293.25V28H292.75ZM289 50.75H234V51.25H289V50.75ZM230.25 47V28H229.75V47H230.25ZM234 50.75C231.929 50.75 230.25 49.0711 230.25 47H229.75C229.75 49.3472 231.653 51.25 234 51.25V50.75ZM292.75 47C292.75 49.0711 291.071 50.75 289 50.75V51.25C291.347 51.25 293.25 49.3472 293.25 47H292.75ZM289 24.25C291.071 24.25 292.75 25.9289 292.75 28H293.25C293.25 25.6528 291.347 23.75 289 23.75V24.25ZM234 23.75C231.653 23.75 229.75 25.6528 229.75 28H230.25C230.25 25.9289 231.929 24.25 234 24.25V23.75Z" fill="#999999"/>
|
|
63
|
-
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.9318 163.527H41.0926C41.8394 163.527 42.4449 164.077 42.4449 164.756V173.244C42.4449 173.923 41.8394 174.473 41.0926 174.473H25.9318C25.185 174.473 24.5795 173.923 24.5795 173.244V173.172C24.5795 173.012 24.4498 172.882 24.2898 172.882C24.1297 172.882 24 173.012 24 173.172V173.244C24 174.214 24.8649 175 25.9318 175H41.0926C42.1595 175 43.0244 174.214 43.0244 173.244V164.756C43.0244 163.786 42.1595 163 41.0926 163H25.9318C24.8649 163 24 163.786 24 164.756V164.828C24 164.988 24.1297 165.118 24.2898 165.118C24.4498 165.118 24.5795 164.988 24.5795 164.828V164.756C24.5795 164.077 25.185 163.527 25.9318 163.527ZM24.0612 172.012C24.1079 172.053 24.1604 172.073 24.2187 172.073H26.5375C27.4008 172.073 28.057 171.877 28.5062 171.485C28.9612 171.087 29.2033 170.481 29.2325 169.667C29.2383 169.503 29.2412 169.281 29.2412 169C29.2412 168.719 29.2383 168.494 29.2325 168.324C29.2091 167.545 28.9612 166.951 28.4887 166.541C28.0162 166.132 27.3512 165.927 26.4937 165.927H24.2187C24.1604 165.927 24.1079 165.947 24.0612 165.988C24.0204 166.029 24 166.082 24 166.146V171.854C24 171.912 24.0204 171.965 24.0612 172.012ZM27.3425 170.537C27.1616 170.718 26.8787 170.809 26.4937 170.809H25.575V167.191H26.45C26.835 167.191 27.1237 167.285 27.3162 167.472C27.5146 167.66 27.6196 167.949 27.6312 168.341C27.6429 168.505 27.6487 168.722 27.6487 168.991C27.6487 169.26 27.6429 169.48 27.6312 169.65C27.6196 170.054 27.5233 170.349 27.3425 170.537ZM30.3246 172.012C30.3713 172.053 30.4238 172.073 30.4821 172.073H31.6546C31.7188 172.073 31.7713 172.053 31.8121 172.012C31.8588 171.971 31.8821 171.918 31.8821 171.854V166.146C31.8821 166.082 31.8588 166.029 31.8121 165.988C31.7713 165.947 31.7188 165.927 31.6546 165.927H30.4821C30.4238 165.927 30.3713 165.947 30.3246 165.988C30.2838 166.029 30.2634 166.082 30.2634 166.146V171.854C30.2634 171.912 30.2838 171.965 30.3246 172.012ZM33.319 172.073C33.2607 172.073 33.2082 172.053 33.1615 172.012C33.1207 171.965 33.1003 171.912 33.1003 171.854V166.146C33.1003 166.082 33.1207 166.029 33.1615 165.988C33.2082 165.947 33.2607 165.927 33.319 165.927H35.594C36.4515 165.927 37.1165 166.132 37.589 166.541C38.0615 166.951 38.3094 167.545 38.3327 168.324C38.3385 168.494 38.3415 168.719 38.3415 169C38.3415 169.281 38.3385 169.503 38.3327 169.667C38.3035 170.481 38.0615 171.087 37.6065 171.485C37.1573 171.877 36.5011 172.073 35.6377 172.073H33.319ZM35.594 170.809C35.979 170.809 36.2619 170.718 36.4427 170.537C36.6236 170.349 36.7198 170.054 36.7315 169.65C36.7431 169.48 36.749 169.26 36.749 168.991C36.749 168.722 36.7431 168.505 36.7315 168.341C36.7198 167.949 36.6148 167.66 36.4165 167.472C36.224 167.285 35.9352 167.191 35.5502 167.191H34.6752V170.809H35.594ZM40.1082 168.282C39.8193 168.282 39.5811 168.047 39.5811 167.759C39.5811 167.47 39.8193 167.235 40.1082 167.235C40.3972 167.235 40.6354 167.47 40.6354 167.759C40.6354 168.047 40.3972 168.282 40.1082 168.282ZM40.1082 171.017C39.8193 171.017 39.5811 170.782 39.5811 170.493C39.5811 170.205 39.8193 169.97 40.1082 169.97C40.3972 169.97 40.6354 170.205 40.6354 170.493C40.6354 170.782 40.3972 171.017 40.1082 171.017Z" fill="#999999"/>
|
|
64
|
-
<text x="48" y="173" font-size="11" fill="#777777" font-family="Courier New">${issuerDid}</text>
|
|
65
|
-
<rect x="0.5" y="0.5" width="316" height="199" rx="7.5" stroke="#E7ECF6"/>
|
|
66
|
-
<defs>
|
|
67
|
-
<linearGradient id="paint0_linear" x1="158.5" y1="0" x2="158.5" y2="200" gradientUnits="userSpaceOnUse">
|
|
68
|
-
<stop stop-color="#F3F6FC"/>
|
|
69
|
-
<stop offset="1" stop-color="#EEF1F7"/>
|
|
70
|
-
</linearGradient>
|
|
71
|
-
</defs>
|
|
72
|
-
</svg>
|
|
73
|
-
`;
|
|
74
|
-
|
|
75
|
-
const createPassportVC = ({ issuerWallet, issuerName, ownerDid, passport, endpoint, types = [], tag } = {}) => {
|
|
56
|
+
const createPassportVC = ({
|
|
57
|
+
issuerWallet,
|
|
58
|
+
issuerName,
|
|
59
|
+
ownerDid,
|
|
60
|
+
passport,
|
|
61
|
+
endpoint,
|
|
62
|
+
types = [],
|
|
63
|
+
tag,
|
|
64
|
+
ownerProfile,
|
|
65
|
+
preferredColor,
|
|
66
|
+
} = {}) => {
|
|
76
67
|
validatePassport(passport);
|
|
77
68
|
|
|
78
69
|
return createVC({
|
|
79
|
-
type: [
|
|
70
|
+
type: [VC_TYPE_GENERAL_PASSPORT, 'VerifiableCredential', ...types].filter(Boolean),
|
|
80
71
|
issuer: {
|
|
81
72
|
wallet: issuerWallet,
|
|
82
73
|
name: issuerName,
|
|
@@ -86,7 +77,14 @@ const createPassportVC = ({ issuerWallet, issuerName, ownerDid, passport, endpoi
|
|
|
86
77
|
passport,
|
|
87
78
|
display: {
|
|
88
79
|
type: 'svg',
|
|
89
|
-
content: createPassportSvg({
|
|
80
|
+
content: createPassportSvg({
|
|
81
|
+
issuer: issuerName,
|
|
82
|
+
issuerDid: issuerWallet.address,
|
|
83
|
+
title: passport.title,
|
|
84
|
+
ownerName: ownerProfile ? ownerProfile.fullName : '',
|
|
85
|
+
ownerAvatarUrl: ownerProfile ? ownerProfile.avatar : '',
|
|
86
|
+
preferredColor,
|
|
87
|
+
}),
|
|
90
88
|
},
|
|
91
89
|
},
|
|
92
90
|
endpoint,
|