UNPKG

@arcblock/did-auth

Version:

Helper function to setup DID authentication support on a node.js web server

257 lines (231 loc) 9.02 kB
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, }; };