@arcblock/did-auth
Version:
Helper function to setup DID authentication support on a node.js web server
257 lines (231 loc) • 9.02 kB
JavaScript
const { Joi } = require('@arcblock/validator');
const { types } = require('@ocap/mcrypto');
const trustedIssuerSchema = Joi.alternatives().try(
Joi.object({
did: Joi.DID().required(),
endpoint: Joi.string()
.uri({ scheme: ['http', 'https'] })
.required(),
}),
Joi.DID().required()
);
const optionalUrlSchema = Joi.string()
.uri({ scheme: ['http', 'https'], allowRelative: true })
.optional()
.default('')
.allow('');
const requirementSchema = Joi.object({
tokens: Joi.array()
.items(
Joi.object({
address: Joi.DID().required(),
value: Joi.BN().positive().required(),
})
)
.required(),
assets: Joi.object({
address: Joi.array().items(Joi.DID()).optional(),
parent: Joi.array().items(Joi.DID()).optional(),
issuer: Joi.array().items(Joi.DID()).optional(),
amount: Joi.number().positive().min(1),
}).optional(),
});
const targetTypeSchema = Joi.object({
key: Joi.string()
.valid(...Object.keys(types.KeyType).map((x) => x.toLowerCase()))
.default('ed25519'),
hash: Joi.string()
.valid(...Object.keys(types.HashType).map((x) => x.toLowerCase()))
.default('sha3'),
role: Joi.string()
.valid(...Object.keys(types.RoleType).map((x) => x.toLowerCase().split('_').pop()))
.default('account'),
encoding: Joi.string()
.valid(...Object.keys(types.EncodingType).map((x) => x.toLowerCase()))
.default('base58'),
}).optional();
// .default({
// key: 'ed25519',
// hash: 'sha3',
// role: 'account',
// encoding: 'base58',
// });
module.exports = (chainInfo) => {
const options = { stripUnknown: true, noDefaults: false };
const createStandardFields = (type, description) => ({
type: Joi.string().valid(type).default(type),
description: Joi.string().min(1).default(description),
chainInfo,
mfaCode: Joi.array().items(Joi.number().min(10).max(99).optional()).default([]),
meta: Joi.any().optional().default({}),
});
// Ask wallet to select a did for later interaction
const authPrincipal = Joi.object({
...createStandardFields('authPrincipal', 'Please continue with your account'),
target: Joi.DID().optional().allow('').default(''),
supervised: Joi.boolean().default(false),
targetType: targetTypeSchema,
}).options(options);
// Ask wallet to create new did and provide sk
const keyPair = Joi.object({
...createStandardFields('keyPair', 'Please create account to continue.'),
moniker: Joi.string()
.regex(/^[a-zA-Z0-9][-a-zA-Z0-9_]{2,128}$/)
.required(),
declare: Joi.boolean().optional().default(true),
migrateFrom: Joi.DID().optional().allow('').default(''),
targetType: targetTypeSchema,
}).options(options);
// Ask wallet to derive encryption key = SHA3(Buffer.concat(keyPair.sk, salt), 3)
const encryptionKey = Joi.object({
...createStandardFields('encryptionKey', 'Please provide encryptionKey to continue.'),
salt: Joi.string().required(),
delegation: Joi.string().optional().allow('').default(''),
}).options(options);
const profile = Joi.object({
...createStandardFields('profile', 'Please provide your profile to continue.'),
items: Joi.array()
.items(Joi.string().valid('did', 'fullName', 'email', 'phone', 'signature', 'avatar', 'birthday', 'url'))
.min(1)
.default(['fullName']),
})
.rename('fields', 'items', { ignoreUndefined: true, override: true })
.options(options);
const signature = Joi.object({
...createStandardFields('signature', 'Sign this transaction or message to continue.'),
typeUrl: Joi.string()
.valid(
'fg:x:delegation',
'fg:t:transaction',
'mime:text/plain',
'mime:text/html',
'eth:transaction',
'eth:standard-data',
'eth:personal-data',
'eth:typed-data',
'eth:legacy-data'
)
.required(),
display: Joi.string().allow('').default(''),
method: Joi.string()
.allow('none', ...Object.keys(types.HashType).map((x) => x.toLowerCase()))
.optional()
.default('sha3'),
digest: Joi.string().allow('').default(''),
origin: Joi.string().allow('').default(''),
nonce: Joi.string().allow('').default(''),
requirement: requirementSchema.optional(),
}).options(options);
const prepareTx = Joi.object({
...createStandardFields('prepareTx', 'Prepare and sign this transaction to continue.'),
display: Joi.string().allow('').default(''),
partialTx: Joi.string().required(),
nonce: Joi.string().allow('').default(''),
requirement: requirementSchema.required(),
}).options(options);
const agreement = Joi.object({
...createStandardFields('agreement', 'Confirm your agreement to continue.'),
uri: Joi.string()
.uri({ scheme: ['http', 'https'] })
.required()
.allow(''),
method: Joi.string()
.allow(...Object.keys(types.HashType).map((x) => x.toLowerCase()))
.optional()
.default('sha2'),
digest: Joi.string().required(),
}).options(options);
const verifiableCredential = Joi.object({
...createStandardFields('verifiableCredential', 'Please present a verifiable credential to continue.'),
optional: Joi.boolean().default(false),
claimUrl: optionalUrlSchema,
acquireUrl: optionalUrlSchema,
// v1
item: Joi.array().items(Joi.string().min(1).required()).min(1).optional(), // alias to type
target: Joi.DID().optional(),
trustedIssuers: Joi.array().items(trustedIssuerSchema).min(1).optional(),
tag: Joi.string().min(1).allow('').default(''),
ownerDid: Joi.array().items(Joi.DID()).optional().default([]),
// v2
// - multiple filter should be interpreted as OR
// - fields inside a filter should be interpreted as AND
// - values inside a filter field should be interpreted as OR
filters: Joi.array()
.items(
Joi.object({
type: Joi.array().items(Joi.string().min(1).required()).min(1).optional(),
target: Joi.DID().optional(),
trustedIssuers: Joi.array().items(trustedIssuerSchema).min(1).optional(),
tag: Joi.string().min(1).allow('').default(''),
ownerDid: Joi.array().items(Joi.DID()).optional().default([]),
claimUrl: optionalUrlSchema,
acquireUrl: optionalUrlSchema,
})
)
.optional(),
}).options(options);
const asset = Joi.object({
...createStandardFields('asset', 'Please present an on chain asset to continue.'),
optional: Joi.boolean().default(false),
// v1
address: Joi.DID().optional(),
trustedIssuers: Joi.array().items(trustedIssuerSchema).min(1).optional(),
trustedParents: Joi.array().items(Joi.DID().required()).min(1).optional(),
tag: Joi.string().min(1).allow('').default(''),
ownerDid: Joi.array().items(Joi.DID()).optional().default([]),
consumed: Joi.boolean().optional(),
acquireUrl: optionalUrlSchema,
// v2
// - multiple filter should be interpreted as OR
// - fields inside a filter should be interpreted as AND
// - values inside a filter field should be interpreted as OR
filters: Joi.array()
.items(
Joi.object({
address: Joi.DID().optional(),
trustedIssuers: Joi.array().items(trustedIssuerSchema).min(1).optional(),
trustedParents: Joi.array().items(Joi.DID().required()).min(1).optional(),
tag: Joi.string().min(1).allow('').default(''),
ownerDid: Joi.array().items(Joi.DID()).optional().default([]),
consumed: Joi.boolean().optional(),
acquireUrl: optionalUrlSchema,
})
)
.optional(),
}).options(options);
const assetOrVC = Joi.object({
...createStandardFields('assetOrVC', 'Please present NFT to continue.'),
// - multiple filter should be interpreted as OR
// - fields inside a filter should be interpreted as AND
// - values inside a filter field should be interpreted as OR
filters: Joi.array()
.items(
Joi.object({
type: Joi.array().items(Joi.string().min(1).required()).min(1).optional(), // by vc type
address: Joi.DID().optional(), // by nft or vc did
trustedIssuers: Joi.array().items(trustedIssuerSchema).min(1).optional(),
trustedParents: Joi.array().items(Joi.DID().required()).min(1).optional(),
tag: Joi.string().min(1).allow('').default(''), // by vc or nft tag
ownerDid: Joi.array().items(Joi.DID()).optional().default([]),
consumed: Joi.boolean().optional(), // only valid for nft
claimUrl: optionalUrlSchema, // only valid for vc
acquireUrl: optionalUrlSchema, // only valid for nft
})
)
.required()
.min(1),
optional: Joi.boolean().default(false),
}).options(options);
return {
authPrincipal,
profile,
signature,
prepareTx,
agreement,
verifiableCredential,
asset,
assetOrVC,
keyPair,
encryptionKey,
};
};