did-jwt-vc
Advanced tools
Comparing version 3.1.4 to 3.2.0
@@ -23,3 +23,3 @@ import { Resolvable } from 'did-resolver'; | ||
*/ | ||
export declare function createVerifiableCredentialJwt(payload: JwtCredentialPayload | CredentialPayload, issuer: Issuer, options?: CreateCredentialOptions): Promise<JWT>; | ||
export declare function createVerifiableCredentialJwt(payload: JwtCredentialPayload | CredentialPayload, issuer: Issuer | Issuer[], options?: CreateCredentialOptions): Promise<JWT>; | ||
/** | ||
@@ -26,0 +26,0 @@ * Creates a VerifiablePresentation JWT given a `PresentationPayload` or `JwtPresentationPayload` and an `Issuer`. |
@@ -1,2 +0,2 @@ | ||
import { decodeJWT, JWT_ERROR, createJWT, verifyJWT } from 'did-jwt'; | ||
import { decodeJWT, JWT_ERROR, createJWT, createMultisignatureJWT, verifyJWT } from 'did-jwt'; | ||
@@ -7,3 +7,2 @@ function _extends() { | ||
var source = arguments[i]; | ||
for (var key in source) { | ||
@@ -15,3 +14,2 @@ if (Object.prototype.hasOwnProperty.call(source, key)) { | ||
} | ||
return target; | ||
@@ -21,3 +19,2 @@ }; | ||
} | ||
function _objectWithoutPropertiesLoose(source, excluded) { | ||
@@ -28,3 +25,2 @@ if (source == null) return {}; | ||
var key, i; | ||
for (i = 0; i < sourceKeys.length; i++) { | ||
@@ -35,3 +31,2 @@ key = sourceKeys[i]; | ||
} | ||
return target; | ||
@@ -54,9 +49,7 @@ } | ||
*/ | ||
const additionalPropNames = ['evidence', 'termsOfUse', 'refreshService', 'credentialSchema', 'credentialStatus']; // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const additionalPropNames = ['evidence', 'termsOfUse', 'refreshService', 'credentialSchema', 'credentialStatus']; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function asArray(arg) { | ||
return Array.isArray(arg) ? arg : [arg]; | ||
} | ||
function deepCopy(source) { | ||
@@ -69,7 +62,5 @@ return Array.isArray(source) ? source.map(item => deepCopy(item)) : source instanceof Date ? new Date(source.getTime()) : source && typeof source === 'object' ? Object.getOwnPropertyNames(source).reduce((o, prop) => { | ||
} | ||
function notEmpty(value) { | ||
return value !== null && value !== undefined; | ||
} | ||
function cleanUndefined(input) { | ||
@@ -79,24 +70,20 @@ if (typeof input !== 'object') { | ||
} | ||
const obj = _extends({}, input); | ||
Object.keys(obj).forEach(key => obj[key] === undefined && delete obj[key]); | ||
return obj; | ||
} // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function isLegacyAttestationFormat(payload) { | ||
// payload is an object and has all the required fields of old attestation format | ||
return typeof payload === 'object' && payload.sub && payload.iss && payload.claim && payload.iat; | ||
} // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function attestationToVcFormat(payload) { | ||
const { | ||
iat, | ||
nbf, | ||
claim, | ||
vc | ||
} = payload, | ||
rest = _objectWithoutPropertiesLoose(payload, _excluded); | ||
iat, | ||
nbf, | ||
claim, | ||
vc | ||
} = payload, | ||
rest = _objectWithoutPropertiesLoose(payload, _excluded); | ||
const result = _extends({}, rest, { | ||
@@ -110,22 +97,15 @@ nbf: nbf ? nbf : iat, | ||
}); | ||
if (vc) payload.issVc = vc; | ||
return result; | ||
} | ||
function normalizeJwtCredentialPayload(input, removeOriginalFields = true) { | ||
var _input$vc, _input$credentialSubj, _result$vc2, _input$vc2; | ||
let result = deepCopy(input); | ||
if (isLegacyAttestationFormat(input)) { | ||
result = attestationToVcFormat(input); | ||
} // FIXME: handle case when credentialSubject(s) are not object types | ||
} | ||
// FIXME: handle case when credentialSubject(s) are not object types | ||
result.credentialSubject = _extends({}, input.credentialSubject, (_input$vc = input.vc) == null ? void 0 : _input$vc.credentialSubject); | ||
if (input.sub && !((_input$credentialSubj = input.credentialSubject) != null && _input$credentialSubj.id) && result.credentialSubject) { | ||
result.credentialSubject.id = input.sub; | ||
if (removeOriginalFields) { | ||
@@ -135,16 +115,11 @@ delete result.sub; | ||
} | ||
if (removeOriginalFields) { | ||
var _result$vc; | ||
(_result$vc = result.vc) == null ? true : delete _result$vc.credentialSubject; | ||
} | ||
if (typeof input.issuer === 'undefined' || typeof input.issuer === 'object') { | ||
var _input$issuer; | ||
result.issuer = cleanUndefined(_extends({ | ||
id: input.iss | ||
}, input.issuer)); | ||
if (removeOriginalFields && !((_input$issuer = input.issuer) != null && _input$issuer.id)) { | ||
@@ -154,6 +129,4 @@ delete result.iss; | ||
} | ||
if (!input.id && input.jti) { | ||
result.id = result.id || result.jti; | ||
if (removeOriginalFields) { | ||
@@ -163,12 +136,8 @@ delete result.jti; | ||
} | ||
const types = [...asArray(result.type), ...asArray((_result$vc2 = result.vc) == null ? void 0 : _result$vc2.type)].filter(notEmpty); | ||
result.type = [...new Set(types)]; | ||
if (removeOriginalFields) { | ||
var _result$vc3; | ||
(_result$vc3 = result.vc) == null ? true : delete _result$vc3.type; | ||
} | ||
for (const prop of additionalPropNames) { | ||
@@ -179,3 +148,2 @@ if (input.vc && input.vc[prop]) { | ||
} | ||
if (removeOriginalFields) { | ||
@@ -186,16 +154,11 @@ delete result.vc[prop]; | ||
} | ||
const contextArray = [...asArray(input.context), ...asArray(input['@context']), ...asArray((_input$vc2 = input.vc) == null ? void 0 : _input$vc2['@context'])].filter(notEmpty); | ||
result['@context'] = [...new Set(contextArray)]; | ||
if (removeOriginalFields) { | ||
var _result$vc4; | ||
delete result.context; | ||
(_result$vc4 = result.vc) == null ? true : delete _result$vc4['@context']; | ||
} | ||
if (!input.issuanceDate && (input.iat || input.nbf)) { | ||
result.issuanceDate = new Date((input.nbf || input.iat) * 1000).toISOString(); | ||
if (removeOriginalFields) { | ||
@@ -209,6 +172,4 @@ if (input.nbf) { | ||
} | ||
if (!input.expirationDate && input.exp) { | ||
result.expirationDate = new Date(input.exp * 1000).toISOString(); | ||
if (removeOriginalFields) { | ||
@@ -218,3 +179,2 @@ delete result.exp; | ||
} | ||
if (removeOriginalFields) { | ||
@@ -224,11 +184,8 @@ if (result.vc && Object.keys(result.vc).length === 0) { | ||
} | ||
} // FIXME: interpret `aud` property as `verifier` | ||
} | ||
// FIXME: interpret `aud` property as `verifier` | ||
return result; | ||
} | ||
function normalizeJwtCredential(input, removeOriginalFields = true) { | ||
let decoded; | ||
try { | ||
@@ -239,3 +196,2 @@ decoded = decodeJWT(input); | ||
} | ||
return _extends({}, normalizeJwtCredentialPayload(decoded.payload, removeOriginalFields), { | ||
@@ -254,7 +210,4 @@ proof: { | ||
*/ | ||
function normalizeCredential(input, removeOriginalFields = true) { | ||
var _input$proof; | ||
if (typeof input === 'string') { | ||
@@ -265,3 +218,2 @@ if (JWT_FORMAT.test(input)) { | ||
let parsed; | ||
try { | ||
@@ -272,3 +224,2 @@ parsed = JSON.parse(input); | ||
} | ||
return normalizeCredential(parsed, removeOriginalFields); | ||
@@ -295,6 +246,4 @@ } | ||
*/ | ||
function transformCredentialInput(input, removeOriginalFields = true) { | ||
var _input$vc3, _input$vc4, _input$vc5; | ||
if (Array.isArray(input.credentialSubject)) throw Error('credentialSubject of type array not supported'); | ||
@@ -305,10 +254,6 @@ const result = deepCopy(_extends({ | ||
result.vc = result.vc; | ||
const credentialSubject = _extends({}, input.credentialSubject, (_input$vc3 = input.vc) == null ? void 0 : _input$vc3.credentialSubject); | ||
if (!input.sub) { | ||
var _input$credentialSubj2; | ||
result.sub = (_input$credentialSubj2 = input.credentialSubject) == null ? void 0 : _input$credentialSubj2.id; | ||
if (removeOriginalFields) { | ||
@@ -318,6 +263,4 @@ delete credentialSubject.id; | ||
} | ||
const contextEntries = [...asArray(input.context), ...asArray(input['@context']), ...asArray((_input$vc4 = input.vc) == null ? void 0 : _input$vc4['@context'])].filter(notEmpty); | ||
result.vc['@context'] = [...new Set(contextEntries)]; | ||
if (removeOriginalFields) { | ||
@@ -327,13 +270,9 @@ delete result.context; | ||
} | ||
const types = [...asArray(input.type), ...asArray((_input$vc5 = input.vc) == null ? void 0 : _input$vc5.type)].filter(notEmpty); | ||
result.vc.type = [...new Set(types)]; | ||
if (removeOriginalFields) { | ||
delete result.type; | ||
} | ||
if (input.id && Object.getOwnPropertyNames(input).indexOf('jti') === -1) { | ||
result.jti = input.id; | ||
if (removeOriginalFields) { | ||
@@ -343,9 +282,6 @@ delete result.id; | ||
} | ||
if (input.issuanceDate && Object.getOwnPropertyNames(input).indexOf('nbf') === -1) { | ||
const converted = Date.parse(input.issuanceDate); | ||
if (!isNaN(converted)) { | ||
result.nbf = Math.floor(converted / 1000); | ||
if (removeOriginalFields) { | ||
@@ -356,9 +292,6 @@ delete result.issuanceDate; | ||
} | ||
if (input.expirationDate && Object.getOwnPropertyNames(input).indexOf('exp') === -1) { | ||
const converted = Date.parse(input.expirationDate); | ||
if (!isNaN(converted)) { | ||
result.exp = Math.floor(converted / 1000); | ||
if (removeOriginalFields) { | ||
@@ -369,12 +302,8 @@ delete result.expirationDate; | ||
} | ||
if (input.issuer && Object.getOwnPropertyNames(input).indexOf('iss') === -1) { | ||
if (typeof input.issuer === 'object') { | ||
var _input$issuer2; | ||
result.iss = (_input$issuer2 = input.issuer) == null ? void 0 : _input$issuer2.id; | ||
if (removeOriginalFields) { | ||
delete result.issuer.id; | ||
if (Object.keys(result.issuer).length === 0) { | ||
@@ -386,3 +315,2 @@ delete result.issuer; | ||
result.iss = input.iss || '' + input.issuer; | ||
if (removeOriginalFields) { | ||
@@ -393,9 +321,6 @@ delete result.issuer; | ||
} | ||
result.vc.credentialSubject = credentialSubject; | ||
if (removeOriginalFields) { | ||
delete result.credentialSubject; | ||
} | ||
for (const prop of additionalPropNames) { | ||
@@ -406,3 +331,2 @@ if (input[prop]) { | ||
} | ||
if (removeOriginalFields) { | ||
@@ -413,9 +337,6 @@ delete result[prop]; | ||
} | ||
return result; | ||
} | ||
function normalizeJwtPresentationPayload(input, removeOriginalFields = true) { | ||
var _input$vp, _input$vp2, _input$vp3; | ||
const result = deepCopy(input); | ||
@@ -426,12 +347,8 @@ result.verifiableCredential = [...asArray(input.verifiableCredential), ...asArray((_input$vp = input.vp) == null ? void 0 : _input$vp.verifiableCredential)].filter(notEmpty); | ||
}); | ||
if (removeOriginalFields) { | ||
var _result$vp; | ||
(_result$vp = result.vp) == null ? true : delete _result$vp.verifiableCredential; | ||
} | ||
if (input.iss && !input.holder) { | ||
result.holder = input.iss; | ||
if (removeOriginalFields) { | ||
@@ -441,7 +358,5 @@ delete result.iss; | ||
} | ||
if (input.aud) { | ||
result.verifier = [...asArray(input.verifier), ...asArray(input.aud)].filter(notEmpty); | ||
result.verifier = [...new Set(result.verifier)]; | ||
if (removeOriginalFields) { | ||
@@ -451,6 +366,4 @@ delete result.aud; | ||
} | ||
if (input.jti && Object.getOwnPropertyNames(input).indexOf('id') === -1) { | ||
result.id = input.id || input.jti; | ||
if (removeOriginalFields) { | ||
@@ -460,25 +373,17 @@ delete result.jti; | ||
} | ||
const types = [...asArray(input.type), ...asArray((_input$vp2 = input.vp) == null ? void 0 : _input$vp2.type)].filter(notEmpty); | ||
result.type = [...new Set(types)]; | ||
if (removeOriginalFields) { | ||
var _result$vp2; | ||
(_result$vp2 = result.vp) == null ? true : delete _result$vp2.type; | ||
} | ||
const contexts = [...asArray(input.context), ...asArray(input['@context']), ...asArray((_input$vp3 = input.vp) == null ? void 0 : _input$vp3['@context'])].filter(notEmpty); | ||
result['@context'] = [...new Set(contexts)]; | ||
if (removeOriginalFields) { | ||
var _result$vp3; | ||
delete result.context; | ||
(_result$vp3 = result.vp) == null ? true : delete _result$vp3['@context']; | ||
} | ||
if (!input.issuanceDate && (input.iat || input.nbf)) { | ||
result.issuanceDate = new Date((input.nbf || input.iat) * 1000).toISOString(); | ||
if (removeOriginalFields) { | ||
@@ -492,6 +397,4 @@ if (input.nbf) { | ||
} | ||
if (!input.expirationDate && input.exp) { | ||
result.expirationDate = new Date(input.exp * 1000).toISOString(); | ||
if (removeOriginalFields) { | ||
@@ -501,3 +404,2 @@ delete result.exp; | ||
} | ||
if (result.vp && Object.keys(result.vp).length === 0) { | ||
@@ -508,9 +410,6 @@ if (removeOriginalFields) { | ||
} | ||
return result; | ||
} | ||
function normalizeJwtPresentation(input, removeOriginalFields = true) { | ||
let decoded; | ||
try { | ||
@@ -521,3 +420,2 @@ decoded = decodeJWT(input); | ||
} | ||
return _extends({}, normalizeJwtPresentationPayload(decoded.payload, removeOriginalFields), { | ||
@@ -534,7 +432,4 @@ proof: { | ||
*/ | ||
function normalizePresentation(input, removeOriginalFields = true) { | ||
var _input$proof2; | ||
if (typeof input === 'string') { | ||
@@ -545,3 +440,2 @@ if (JWT_FORMAT.test(input)) { | ||
let parsed; | ||
try { | ||
@@ -552,3 +446,2 @@ parsed = JSON.parse(input); | ||
} | ||
return normalizePresentation(parsed, removeOriginalFields); | ||
@@ -575,6 +468,4 @@ } | ||
*/ | ||
function transformPresentationInput(input, removeOriginalFields = true) { | ||
var _input$vp4, _input$vp5, _result$vp4; | ||
const result = deepCopy(_extends({ | ||
@@ -586,3 +477,2 @@ vp: _extends({}, input.vp) | ||
result.vp['@context'] = [...new Set(contextEntries)]; | ||
if (removeOriginalFields) { | ||
@@ -592,13 +482,9 @@ delete result.context; | ||
} | ||
const types = [...asArray(input.type), ...asArray((_input$vp5 = input.vp) == null ? void 0 : _input$vp5.type)].filter(notEmpty); | ||
result.vp.type = [...new Set(types)]; | ||
if (removeOriginalFields) { | ||
delete result.type; | ||
} | ||
if (input.id && Object.getOwnPropertyNames(input).indexOf('jti') === -1) { | ||
result.jti = input.id; | ||
if (removeOriginalFields) { | ||
@@ -608,9 +494,6 @@ delete result.id; | ||
} | ||
if (input.issuanceDate && Object.getOwnPropertyNames(input).indexOf('nbf') === -1) { | ||
const converted = Date.parse(input.issuanceDate); | ||
if (!isNaN(converted)) { | ||
result.nbf = Math.floor(converted / 1000); | ||
if (removeOriginalFields) { | ||
@@ -621,9 +504,6 @@ delete result.issuanceDate; | ||
} | ||
if (input.expirationDate && Object.getOwnPropertyNames(input).indexOf('exp') === -1) { | ||
const converted = Date.parse(input.expirationDate); | ||
if (!isNaN(converted)) { | ||
result.exp = Math.floor(converted / 1000); | ||
if (removeOriginalFields) { | ||
@@ -634,9 +514,6 @@ delete result.expirationDate; | ||
} | ||
if (result.verifiableCredential || (_result$vp4 = result.vp) != null && _result$vp4.verifiableCredential) { | ||
var _result$vp5; | ||
result.vp.verifiableCredential = [...asArray(result.verifiableCredential), ...asArray((_result$vp5 = result.vp) == null ? void 0 : _result$vp5.verifiableCredential)].filter(notEmpty).map(credential => { | ||
var _credential$proof; | ||
if (typeof credential === 'object' && (_credential$proof = credential.proof) != null && _credential$proof.jwt) { | ||
@@ -649,11 +526,8 @@ return credential.proof.jwt; | ||
} | ||
if (removeOriginalFields) { | ||
delete result.verifiableCredential; | ||
} | ||
if (input.holder && Object.getOwnPropertyNames(input).indexOf('iss') === -1) { | ||
if (typeof input.holder === 'string') { | ||
result.iss = input.holder; | ||
if (removeOriginalFields) { | ||
@@ -664,7 +538,5 @@ delete result.holder; | ||
} | ||
if (input.verifier) { | ||
const audience = [...asArray(input.verifier), ...asArray(input.aud)].filter(notEmpty); | ||
result.aud = [...new Set(audience)]; | ||
if (removeOriginalFields) { | ||
@@ -674,3 +546,2 @@ delete result.verifier; | ||
} | ||
return result; | ||
@@ -683,3 +554,2 @@ } | ||
*/ | ||
const VC_ERROR = { | ||
@@ -691,3 +561,2 @@ /** | ||
SCHEMA_ERROR: 'schema_error', | ||
/** | ||
@@ -697,3 +566,2 @@ * Thrown when the input is not a JWT string | ||
FORMAT_ERROR: 'format_error', | ||
/** | ||
@@ -707,9 +575,7 @@ * Thrown when verifying a presentation where `challenge` and/or `domain` don't match the expected values. | ||
*/ | ||
const VC_JWT_ERROR = _extends({}, VC_ERROR, JWT_ERROR); // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const VC_JWT_ERROR = _extends({}, VC_ERROR, JWT_ERROR); | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function isDateObject(input) { | ||
return input && !isNaN(input) && Object.prototype.toString.call(input) === '[object Date]'; | ||
} | ||
function validateJwtFormat(value) { | ||
@@ -719,3 +585,4 @@ if (typeof value === 'string' && !value.match(JWT_FORMAT)) { | ||
} | ||
} // The main scenario we want to guard against is having a timestamp in milliseconds | ||
} | ||
// The main scenario we want to guard against is having a timestamp in milliseconds | ||
// instead of seconds (ex: from new Date().getTime()). | ||
@@ -727,3 +594,2 @@ // We will check the number of digits and assume that any number with 12 or more | ||
// 12 digits max is 999999999999 -> 09/27/33658 @ 1:46am (UTC) | ||
function validateTimestamp(value) { | ||
@@ -742,3 +608,2 @@ if (typeof value === 'number') { | ||
const input = asArray(value); | ||
if (input.length < 1 || input.indexOf(DEFAULT_CONTEXT) === -1) { | ||
@@ -750,3 +615,2 @@ throw new TypeError(`${VC_ERROR.SCHEMA_ERROR}: @context is missing default context "${DEFAULT_CONTEXT}"`); | ||
const input = asArray(value); | ||
if (input.length < 1 || input.indexOf(DEFAULT_VC_TYPE) === -1) { | ||
@@ -758,3 +622,2 @@ throw new TypeError(`${VC_ERROR.SCHEMA_ERROR}: type is missing default "${DEFAULT_VC_TYPE}"`); | ||
const input = asArray(value); | ||
if (input.length < 1 || input.indexOf(DEFAULT_VP_TYPE) === -1) { | ||
@@ -786,17 +649,31 @@ throw new TypeError(`${VC_ERROR.SCHEMA_ERROR}: type is missing default "${DEFAULT_VP_TYPE}"`); | ||
*/ | ||
async function createVerifiableCredentialJwt(payload, issuer, options = {}) { | ||
var _options$header; | ||
const parsedPayload = _extends({ | ||
iat: undefined | ||
}, transformCredentialInput(payload, options.removeOriginalFields)); | ||
validateJwtCredentialPayload(parsedPayload); | ||
return createJWT(parsedPayload, _extends({}, options, { | ||
issuer: issuer.did || parsedPayload.iss || '', | ||
signer: issuer.signer | ||
}), _extends({}, options.header, { | ||
alg: issuer.alg || ((_options$header = options.header) == null ? void 0 : _options$header.alg) || JWT_ALG | ||
})); | ||
if (!Array.isArray(issuer)) { | ||
var _options$header; | ||
return createJWT(parsedPayload, _extends({}, options, { | ||
issuer: issuer.did || parsedPayload.iss || '', | ||
signer: issuer.signer | ||
}), _extends({}, options.header, { | ||
alg: issuer.alg || ((_options$header = options.header) == null ? void 0 : _options$header.alg) || JWT_ALG | ||
})); | ||
} else { | ||
const did = issuer[0].did; | ||
const issuers = []; | ||
for (const iss of issuer) { | ||
var _options$header2; | ||
if (iss.did !== did) { | ||
throw new Error('All issuers must be the same did to comply with the Verifiable Conditions spec'); | ||
} | ||
issuers.push({ | ||
issuer: iss.did || parsedPayload.iss || '', | ||
signer: iss.signer, | ||
alg: iss.alg || ((_options$header2 = options.header) == null ? void 0 : _options$header2.alg) || JWT_ALG | ||
}); | ||
} | ||
return createMultisignatureJWT(parsedPayload, _extends({}, options), issuers); | ||
} | ||
} | ||
@@ -820,16 +697,12 @@ /** | ||
*/ | ||
async function createVerifiablePresentationJwt(payload, holder, options = {}) { | ||
var _options$header2; | ||
var _options$header3; | ||
const parsedPayload = _extends({ | ||
iat: undefined | ||
}, transformPresentationInput(payload, options == null ? void 0 : options.removeOriginalFields)); // add challenge to nonce | ||
}, transformPresentationInput(payload, options == null ? void 0 : options.removeOriginalFields)); | ||
// add challenge to nonce | ||
if (options.challenge && Object.getOwnPropertyNames(parsedPayload).indexOf('nonce') === -1) { | ||
parsedPayload.nonce = options.challenge; | ||
} // add domain to audience. | ||
} | ||
// add domain to audience. | ||
if (options.domain) { | ||
@@ -839,3 +712,2 @@ const audience = [...asArray(options.domain), ...asArray(parsedPayload.aud)].filter(notEmpty); | ||
} | ||
validateJwtPresentationPayload(parsedPayload); | ||
@@ -846,3 +718,3 @@ return createJWT(parsedPayload, _extends({}, options, { | ||
}), _extends({}, options.header, { | ||
alg: holder.alg || ((_options$header2 = options.header) == null ? void 0 : _options$header2.alg) || JWT_ALG | ||
alg: holder.alg || ((_options$header3 = options.header) == null ? void 0 : _options$header3.alg) || JWT_ALG | ||
})); | ||
@@ -866,4 +738,4 @@ } | ||
validateContext(payload.vp['@context']); | ||
validateVpType(payload.vp.type); // empty credential array is allowed | ||
validateVpType(payload.vp.type); | ||
// empty credential array is allowed | ||
if (payload.vp.verifiableCredential && payload.vp.verifiableCredential.length >= 1) { | ||
@@ -878,3 +750,2 @@ for (const vc of asArray(payload.vp.verifiableCredential)) { | ||
} | ||
if (payload.exp) validateTimestamp(payload.exp); | ||
@@ -884,4 +755,4 @@ } | ||
validateContext(payload['@context']); | ||
validateVpType(payload.type); // empty credential array is allowed | ||
validateVpType(payload.type); | ||
// empty credential array is allowed | ||
if (payload.verifiableCredential && payload.verifiableCredential.length >= 1) { | ||
@@ -896,3 +767,2 @@ for (const vc of payload.verifiableCredential) { | ||
} | ||
if (payload.expirationDate) validateTimestamp(payload.expirationDate); | ||
@@ -910,6 +780,4 @@ } | ||
*/ | ||
async function verifyCredential(vc, resolver, options = {}) { | ||
var _options, _options$policies, _options2, _options2$policies, _options3, _options4, _options5, _options5$policies; | ||
const nbf = ((_options = options) == null ? void 0 : (_options$policies = _options.policies) == null ? void 0 : _options$policies.issuanceDate) === false ? false : undefined; | ||
@@ -928,7 +796,5 @@ const exp = ((_options2 = options) == null ? void 0 : (_options2$policies = _options2.policies) == null ? void 0 : _options2$policies.expirationDate) === false ? false : undefined; | ||
verified.verifiableCredential = normalizeCredential(verified.jwt, (_options4 = options) == null ? void 0 : _options4.removeOriginalFields); | ||
if (((_options5 = options) == null ? void 0 : (_options5$policies = _options5.policies) == null ? void 0 : _options5$policies.format) !== false) { | ||
validateCredentialPayload(verified.verifiableCredential); | ||
} | ||
return verified; | ||
@@ -943,3 +809,2 @@ } | ||
*/ | ||
function verifyPresentationPayloadOptions(payload, options) { | ||
@@ -949,7 +814,5 @@ if (options.challenge && options.challenge !== payload.nonce) { | ||
} | ||
if (options.domain) { | ||
// aud might be an array | ||
let matchedAudience; | ||
if (payload.aud) { | ||
@@ -959,3 +822,2 @@ const audArray = Array.isArray(payload.aud) ? payload.aud : [payload.aud]; | ||
} | ||
if (typeof matchedAudience === 'undefined') { | ||
@@ -976,6 +838,4 @@ throw new Error(`${VC_ERROR.AUTH_ERROR}: Presentation does not contain the mandatory domain (JWT: aud) for : ${options.domain}`); | ||
*/ | ||
async function verifyPresentation(presentation, resolver, options = {}) { | ||
var _options6, _options6$policies, _options7, _options7$policies, _options8, _options9, _options10, _options10$policies; | ||
const nbf = ((_options6 = options) == null ? void 0 : (_options6$policies = _options6.policies) == null ? void 0 : _options6$policies.issuanceDate) === false ? false : undefined; | ||
@@ -997,7 +857,5 @@ const exp = ((_options7 = options) == null ? void 0 : (_options7$policies = _options7.policies) == null ? void 0 : _options7$policies.expirationDate) === false ? false : undefined; | ||
verified.verifiablePresentation = normalizePresentation(verified.jwt, (_options9 = options) == null ? void 0 : _options9.removeOriginalFields); | ||
if (((_options10 = options) == null ? void 0 : (_options10$policies = _options10.policies) == null ? void 0 : _options10$policies.format) !== false) { | ||
validatePresentationPayload(verified.verifiablePresentation); | ||
} | ||
return verified; | ||
@@ -1004,0 +862,0 @@ } |
@@ -1,2 +0,2 @@ | ||
import { decodeJWT, JWT_ERROR, verifyJWT, createJWT } from 'did-jwt'; | ||
import { decodeJWT, JWT_ERROR, verifyJWT, createJWT, createMultisignatureJWT } from 'did-jwt'; | ||
@@ -16,9 +16,7 @@ const JWT_ALG = 'ES256K'; | ||
*/ | ||
const additionalPropNames = ['evidence', 'termsOfUse', 'refreshService', 'credentialSchema', 'credentialStatus']; // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const additionalPropNames = ['evidence', 'termsOfUse', 'refreshService', 'credentialSchema', 'credentialStatus']; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function asArray(arg) { | ||
return Array.isArray(arg) ? arg : [arg]; | ||
} | ||
function deepCopy(source) { | ||
@@ -31,7 +29,5 @@ return Array.isArray(source) ? source.map(item => deepCopy(item)) : source instanceof Date ? new Date(source.getTime()) : source && typeof source === 'object' ? Object.getOwnPropertyNames(source).reduce((o, prop) => { | ||
} | ||
function notEmpty(value) { | ||
return value !== null && value !== undefined; | ||
} | ||
function cleanUndefined(input) { | ||
@@ -41,15 +37,14 @@ if (typeof input !== 'object') { | ||
} | ||
const obj = { ...input | ||
const obj = { | ||
...input | ||
}; | ||
Object.keys(obj).forEach(key => obj[key] === undefined && delete obj[key]); | ||
return obj; | ||
} // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function isLegacyAttestationFormat(payload) { | ||
// payload is an object and has all the required fields of old attestation format | ||
return typeof payload === 'object' && payload.sub && payload.iss && payload.claim && payload.iat; | ||
} // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function attestationToVcFormat(payload) { | ||
@@ -63,3 +58,4 @@ const { | ||
} = payload; | ||
const result = { ...rest, | ||
const result = { | ||
...rest, | ||
nbf: nbf ? nbf : iat, | ||
@@ -75,18 +71,14 @@ vc: { | ||
} | ||
function normalizeJwtCredentialPayload(input, removeOriginalFields = true) { | ||
let result = deepCopy(input); | ||
if (isLegacyAttestationFormat(input)) { | ||
result = attestationToVcFormat(input); | ||
} // FIXME: handle case when credentialSubject(s) are not object types | ||
result.credentialSubject = { ...input.credentialSubject, | ||
} | ||
// FIXME: handle case when credentialSubject(s) are not object types | ||
result.credentialSubject = { | ||
...input.credentialSubject, | ||
...input.vc?.credentialSubject | ||
}; | ||
if (input.sub && !input.credentialSubject?.id && result.credentialSubject) { | ||
result.credentialSubject.id = input.sub; | ||
if (removeOriginalFields) { | ||
@@ -96,7 +88,5 @@ delete result.sub; | ||
} | ||
if (removeOriginalFields) { | ||
delete result.vc?.credentialSubject; | ||
} | ||
if (typeof input.issuer === 'undefined' || typeof input.issuer === 'object') { | ||
@@ -107,3 +97,2 @@ result.issuer = cleanUndefined({ | ||
}); | ||
if (removeOriginalFields && !input.issuer?.id) { | ||
@@ -113,6 +102,4 @@ delete result.iss; | ||
} | ||
if (!input.id && input.jti) { | ||
result.id = result.id || result.jti; | ||
if (removeOriginalFields) { | ||
@@ -122,10 +109,7 @@ delete result.jti; | ||
} | ||
const types = [...asArray(result.type), ...asArray(result.vc?.type)].filter(notEmpty); | ||
result.type = [...new Set(types)]; | ||
if (removeOriginalFields) { | ||
delete result.vc?.type; | ||
} | ||
for (const prop of additionalPropNames) { | ||
@@ -136,3 +120,2 @@ if (input.vc && input.vc[prop]) { | ||
} | ||
if (removeOriginalFields) { | ||
@@ -143,6 +126,4 @@ delete result.vc[prop]; | ||
} | ||
const contextArray = [...asArray(input.context), ...asArray(input['@context']), ...asArray(input.vc?.['@context'])].filter(notEmpty); | ||
result['@context'] = [...new Set(contextArray)]; | ||
if (removeOriginalFields) { | ||
@@ -152,6 +133,4 @@ delete result.context; | ||
} | ||
if (!input.issuanceDate && (input.iat || input.nbf)) { | ||
result.issuanceDate = new Date((input.nbf || input.iat) * 1000).toISOString(); | ||
if (removeOriginalFields) { | ||
@@ -165,6 +144,4 @@ if (input.nbf) { | ||
} | ||
if (!input.expirationDate && input.exp) { | ||
result.expirationDate = new Date(input.exp * 1000).toISOString(); | ||
if (removeOriginalFields) { | ||
@@ -174,3 +151,2 @@ delete result.exp; | ||
} | ||
if (removeOriginalFields) { | ||
@@ -180,11 +156,8 @@ if (result.vc && Object.keys(result.vc).length === 0) { | ||
} | ||
} // FIXME: interpret `aud` property as `verifier` | ||
} | ||
// FIXME: interpret `aud` property as `verifier` | ||
return result; | ||
} | ||
function normalizeJwtCredential(input, removeOriginalFields = true) { | ||
let decoded; | ||
try { | ||
@@ -195,4 +168,4 @@ decoded = decodeJWT(input); | ||
} | ||
return { ...normalizeJwtCredentialPayload(decoded.payload, removeOriginalFields), | ||
return { | ||
...normalizeJwtCredentialPayload(decoded.payload, removeOriginalFields), | ||
proof: { | ||
@@ -210,4 +183,2 @@ type: DEFAULT_JWT_PROOF_TYPE, | ||
*/ | ||
function normalizeCredential(input, removeOriginalFields = true) { | ||
@@ -219,3 +190,2 @@ if (typeof input === 'string') { | ||
let parsed; | ||
try { | ||
@@ -226,3 +196,2 @@ parsed = JSON.parse(input); | ||
} | ||
return normalizeCredential(parsed, removeOriginalFields); | ||
@@ -232,3 +201,4 @@ } | ||
// TODO: test that it correctly propagates app specific proof properties | ||
return deepCopy({ ...normalizeJwtCredential(input.proof.jwt, removeOriginalFields), | ||
return deepCopy({ | ||
...normalizeJwtCredential(input.proof.jwt, removeOriginalFields), | ||
proof: input.proof | ||
@@ -251,7 +221,7 @@ }); | ||
*/ | ||
function transformCredentialInput(input, removeOriginalFields = true) { | ||
if (Array.isArray(input.credentialSubject)) throw Error('credentialSubject of type array not supported'); | ||
const result = deepCopy({ | ||
vc: { ...input.vc | ||
vc: { | ||
...input.vc | ||
}, | ||
@@ -261,9 +231,8 @@ ...input | ||
result.vc = result.vc; | ||
const credentialSubject = { ...input.credentialSubject, | ||
const credentialSubject = { | ||
...input.credentialSubject, | ||
...input.vc?.credentialSubject | ||
}; | ||
if (!input.sub) { | ||
result.sub = input.credentialSubject?.id; | ||
if (removeOriginalFields) { | ||
@@ -273,6 +242,4 @@ delete credentialSubject.id; | ||
} | ||
const contextEntries = [...asArray(input.context), ...asArray(input['@context']), ...asArray(input.vc?.['@context'])].filter(notEmpty); | ||
result.vc['@context'] = [...new Set(contextEntries)]; | ||
if (removeOriginalFields) { | ||
@@ -282,13 +249,9 @@ delete result.context; | ||
} | ||
const types = [...asArray(input.type), ...asArray(input.vc?.type)].filter(notEmpty); | ||
result.vc.type = [...new Set(types)]; | ||
if (removeOriginalFields) { | ||
delete result.type; | ||
} | ||
if (input.id && Object.getOwnPropertyNames(input).indexOf('jti') === -1) { | ||
result.jti = input.id; | ||
if (removeOriginalFields) { | ||
@@ -298,9 +261,6 @@ delete result.id; | ||
} | ||
if (input.issuanceDate && Object.getOwnPropertyNames(input).indexOf('nbf') === -1) { | ||
const converted = Date.parse(input.issuanceDate); | ||
if (!isNaN(converted)) { | ||
result.nbf = Math.floor(converted / 1000); | ||
if (removeOriginalFields) { | ||
@@ -311,9 +271,6 @@ delete result.issuanceDate; | ||
} | ||
if (input.expirationDate && Object.getOwnPropertyNames(input).indexOf('exp') === -1) { | ||
const converted = Date.parse(input.expirationDate); | ||
if (!isNaN(converted)) { | ||
result.exp = Math.floor(converted / 1000); | ||
if (removeOriginalFields) { | ||
@@ -324,10 +281,7 @@ delete result.expirationDate; | ||
} | ||
if (input.issuer && Object.getOwnPropertyNames(input).indexOf('iss') === -1) { | ||
if (typeof input.issuer === 'object') { | ||
result.iss = input.issuer?.id; | ||
if (removeOriginalFields) { | ||
delete result.issuer.id; | ||
if (Object.keys(result.issuer).length === 0) { | ||
@@ -339,3 +293,2 @@ delete result.issuer; | ||
result.iss = input.iss || '' + input.issuer; | ||
if (removeOriginalFields) { | ||
@@ -346,9 +299,6 @@ delete result.issuer; | ||
} | ||
result.vc.credentialSubject = credentialSubject; | ||
if (removeOriginalFields) { | ||
delete result.credentialSubject; | ||
} | ||
for (const prop of additionalPropNames) { | ||
@@ -359,3 +309,2 @@ if (input[prop]) { | ||
} | ||
if (removeOriginalFields) { | ||
@@ -366,6 +315,4 @@ delete result[prop]; | ||
} | ||
return result; | ||
} | ||
function normalizeJwtPresentationPayload(input, removeOriginalFields = true) { | ||
@@ -377,10 +324,7 @@ const result = deepCopy(input); | ||
}); | ||
if (removeOriginalFields) { | ||
delete result.vp?.verifiableCredential; | ||
} | ||
if (input.iss && !input.holder) { | ||
result.holder = input.iss; | ||
if (removeOriginalFields) { | ||
@@ -390,7 +334,5 @@ delete result.iss; | ||
} | ||
if (input.aud) { | ||
result.verifier = [...asArray(input.verifier), ...asArray(input.aud)].filter(notEmpty); | ||
result.verifier = [...new Set(result.verifier)]; | ||
if (removeOriginalFields) { | ||
@@ -400,6 +342,4 @@ delete result.aud; | ||
} | ||
if (input.jti && Object.getOwnPropertyNames(input).indexOf('id') === -1) { | ||
result.id = input.id || input.jti; | ||
if (removeOriginalFields) { | ||
@@ -409,13 +349,9 @@ delete result.jti; | ||
} | ||
const types = [...asArray(input.type), ...asArray(input.vp?.type)].filter(notEmpty); | ||
result.type = [...new Set(types)]; | ||
if (removeOriginalFields) { | ||
delete result.vp?.type; | ||
} | ||
const contexts = [...asArray(input.context), ...asArray(input['@context']), ...asArray(input.vp?.['@context'])].filter(notEmpty); | ||
result['@context'] = [...new Set(contexts)]; | ||
if (removeOriginalFields) { | ||
@@ -425,6 +361,4 @@ delete result.context; | ||
} | ||
if (!input.issuanceDate && (input.iat || input.nbf)) { | ||
result.issuanceDate = new Date((input.nbf || input.iat) * 1000).toISOString(); | ||
if (removeOriginalFields) { | ||
@@ -438,6 +372,4 @@ if (input.nbf) { | ||
} | ||
if (!input.expirationDate && input.exp) { | ||
result.expirationDate = new Date(input.exp * 1000).toISOString(); | ||
if (removeOriginalFields) { | ||
@@ -447,3 +379,2 @@ delete result.exp; | ||
} | ||
if (result.vp && Object.keys(result.vp).length === 0) { | ||
@@ -454,9 +385,6 @@ if (removeOriginalFields) { | ||
} | ||
return result; | ||
} | ||
function normalizeJwtPresentation(input, removeOriginalFields = true) { | ||
let decoded; | ||
try { | ||
@@ -467,4 +395,4 @@ decoded = decodeJWT(input); | ||
} | ||
return { ...normalizeJwtPresentationPayload(decoded.payload, removeOriginalFields), | ||
return { | ||
...normalizeJwtPresentationPayload(decoded.payload, removeOriginalFields), | ||
proof: { | ||
@@ -480,4 +408,2 @@ type: DEFAULT_JWT_PROOF_TYPE, | ||
*/ | ||
function normalizePresentation(input, removeOriginalFields = true) { | ||
@@ -489,3 +415,2 @@ if (typeof input === 'string') { | ||
let parsed; | ||
try { | ||
@@ -496,3 +421,2 @@ parsed = JSON.parse(input); | ||
} | ||
return normalizePresentation(parsed, removeOriginalFields); | ||
@@ -502,3 +426,4 @@ } | ||
// TODO: test that it correctly propagates app specific proof properties | ||
return { ...normalizeJwtPresentation(input.proof.jwt, removeOriginalFields), | ||
return { | ||
...normalizeJwtPresentation(input.proof.jwt, removeOriginalFields), | ||
proof: input.proof | ||
@@ -521,6 +446,6 @@ }; | ||
*/ | ||
function transformPresentationInput(input, removeOriginalFields = true) { | ||
const result = deepCopy({ | ||
vp: { ...input.vp | ||
vp: { | ||
...input.vp | ||
}, | ||
@@ -532,3 +457,2 @@ ...input | ||
result.vp['@context'] = [...new Set(contextEntries)]; | ||
if (removeOriginalFields) { | ||
@@ -538,13 +462,9 @@ delete result.context; | ||
} | ||
const types = [...asArray(input.type), ...asArray(input.vp?.type)].filter(notEmpty); | ||
result.vp.type = [...new Set(types)]; | ||
if (removeOriginalFields) { | ||
delete result.type; | ||
} | ||
if (input.id && Object.getOwnPropertyNames(input).indexOf('jti') === -1) { | ||
result.jti = input.id; | ||
if (removeOriginalFields) { | ||
@@ -554,9 +474,6 @@ delete result.id; | ||
} | ||
if (input.issuanceDate && Object.getOwnPropertyNames(input).indexOf('nbf') === -1) { | ||
const converted = Date.parse(input.issuanceDate); | ||
if (!isNaN(converted)) { | ||
result.nbf = Math.floor(converted / 1000); | ||
if (removeOriginalFields) { | ||
@@ -567,9 +484,6 @@ delete result.issuanceDate; | ||
} | ||
if (input.expirationDate && Object.getOwnPropertyNames(input).indexOf('exp') === -1) { | ||
const converted = Date.parse(input.expirationDate); | ||
if (!isNaN(converted)) { | ||
result.exp = Math.floor(converted / 1000); | ||
if (removeOriginalFields) { | ||
@@ -580,3 +494,2 @@ delete result.expirationDate; | ||
} | ||
if (result.verifiableCredential || result.vp?.verifiableCredential) { | ||
@@ -591,11 +504,8 @@ result.vp.verifiableCredential = [...asArray(result.verifiableCredential), ...asArray(result.vp?.verifiableCredential)].filter(notEmpty).map(credential => { | ||
} | ||
if (removeOriginalFields) { | ||
delete result.verifiableCredential; | ||
} | ||
if (input.holder && Object.getOwnPropertyNames(input).indexOf('iss') === -1) { | ||
if (typeof input.holder === 'string') { | ||
result.iss = input.holder; | ||
if (removeOriginalFields) { | ||
@@ -606,7 +516,5 @@ delete result.holder; | ||
} | ||
if (input.verifier) { | ||
const audience = [...asArray(input.verifier), ...asArray(input.aud)].filter(notEmpty); | ||
result.aud = [...new Set(audience)]; | ||
if (removeOriginalFields) { | ||
@@ -616,3 +524,2 @@ delete result.verifier; | ||
} | ||
return result; | ||
@@ -625,3 +532,2 @@ } | ||
*/ | ||
const VC_ERROR = { | ||
@@ -633,3 +539,2 @@ /** | ||
SCHEMA_ERROR: 'schema_error', | ||
/** | ||
@@ -639,3 +544,2 @@ * Thrown when the input is not a JWT string | ||
FORMAT_ERROR: 'format_error', | ||
/** | ||
@@ -649,11 +553,10 @@ * Thrown when verifying a presentation where `challenge` and/or `domain` don't match the expected values. | ||
*/ | ||
const VC_JWT_ERROR = { ...VC_ERROR, | ||
const VC_JWT_ERROR = { | ||
...VC_ERROR, | ||
...JWT_ERROR | ||
}; // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
}; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function isDateObject(input) { | ||
return input && !isNaN(input) && Object.prototype.toString.call(input) === '[object Date]'; | ||
} | ||
function validateJwtFormat(value) { | ||
@@ -663,3 +566,4 @@ if (typeof value === 'string' && !value.match(JWT_FORMAT)) { | ||
} | ||
} // The main scenario we want to guard against is having a timestamp in milliseconds | ||
} | ||
// The main scenario we want to guard against is having a timestamp in milliseconds | ||
// instead of seconds (ex: from new Date().getTime()). | ||
@@ -671,3 +575,2 @@ // We will check the number of digits and assume that any number with 12 or more | ||
// 12 digits max is 999999999999 -> 09/27/33658 @ 1:46am (UTC) | ||
function validateTimestamp(value) { | ||
@@ -686,3 +589,2 @@ if (typeof value === 'number') { | ||
const input = asArray(value); | ||
if (input.length < 1 || input.indexOf(DEFAULT_CONTEXT) === -1) { | ||
@@ -694,3 +596,2 @@ throw new TypeError(`${VC_ERROR.SCHEMA_ERROR}: @context is missing default context "${DEFAULT_CONTEXT}"`); | ||
const input = asArray(value); | ||
if (input.length < 1 || input.indexOf(DEFAULT_VC_TYPE) === -1) { | ||
@@ -702,3 +603,2 @@ throw new TypeError(`${VC_ERROR.SCHEMA_ERROR}: type is missing default "${DEFAULT_VC_TYPE}"`); | ||
const input = asArray(value); | ||
if (input.length < 1 || input.indexOf(DEFAULT_VP_TYPE) === -1) { | ||
@@ -731,3 +631,4 @@ throw new TypeError(`${VC_ERROR.SCHEMA_ERROR}: type is missing default "${DEFAULT_VP_TYPE}"`); | ||
...options, | ||
policies: { ...options?.policies, | ||
policies: { | ||
...options?.policies, | ||
nbf, | ||
@@ -744,7 +645,5 @@ exp, | ||
verified.verifiablePresentation = normalizePresentation(verified.jwt, options?.removeOriginalFields); | ||
if (options?.policies?.format !== false) { | ||
validatePresentationPayload(verified.verifiablePresentation); | ||
} | ||
return verified; | ||
@@ -756,3 +655,2 @@ }); | ||
}; | ||
/** | ||
@@ -772,4 +670,6 @@ * Verifies and validates a VerifiableCredential that is encoded as a JWT according to the W3C spec. | ||
const exp = options?.policies?.expirationDate === false ? false : undefined; | ||
options = { ...options, | ||
policies: { ...options?.policies, | ||
options = { | ||
...options, | ||
policies: { | ||
...options?.policies, | ||
nbf, | ||
@@ -785,7 +685,5 @@ exp, | ||
verified.verifiableCredential = normalizeCredential(verified.jwt, options?.removeOriginalFields); | ||
if (options?.policies?.format !== false) { | ||
validateCredentialPayload(verified.verifiableCredential); | ||
} | ||
return verified; | ||
@@ -804,3 +702,2 @@ }); | ||
*/ | ||
/** | ||
@@ -828,9 +725,8 @@ * Creates a VerifiablePresentation JWT given a `PresentationPayload` or `JwtPresentationPayload` and an `Issuer`. | ||
...transformPresentationInput(payload, options?.removeOriginalFields) | ||
}; // add challenge to nonce | ||
}; | ||
// add challenge to nonce | ||
if (options.challenge && Object.getOwnPropertyNames(parsedPayload).indexOf('nonce') === -1) { | ||
parsedPayload.nonce = options.challenge; | ||
} // add domain to audience. | ||
} | ||
// add domain to audience. | ||
if (options.domain) { | ||
@@ -840,8 +736,9 @@ const audience = [...asArray(options.domain), ...asArray(parsedPayload.aud)].filter(notEmpty); | ||
} | ||
validateJwtPresentationPayload(parsedPayload); | ||
return Promise.resolve(createJWT(parsedPayload, { ...options, | ||
return Promise.resolve(createJWT(parsedPayload, { | ||
...options, | ||
issuer: holder.did || parsedPayload.iss || '', | ||
signer: holder.signer | ||
}, { ...options.header, | ||
}, { | ||
...options.header, | ||
alg: holder.alg || options.header?.alg || JWT_ALG | ||
@@ -853,3 +750,2 @@ })); | ||
}; | ||
/** | ||
@@ -878,8 +774,28 @@ * Creates a VerifiableCredential given a `CredentialPayload` or `JwtCredentialPayload` and an `Issuer`. | ||
validateJwtCredentialPayload(parsedPayload); | ||
return Promise.resolve(createJWT(parsedPayload, { ...options, | ||
issuer: issuer.did || parsedPayload.iss || '', | ||
signer: issuer.signer | ||
}, { ...options.header, | ||
alg: issuer.alg || options.header?.alg || JWT_ALG | ||
})); | ||
if (!Array.isArray(issuer)) { | ||
return Promise.resolve(createJWT(parsedPayload, { | ||
...options, | ||
issuer: issuer.did || parsedPayload.iss || '', | ||
signer: issuer.signer | ||
}, { | ||
...options.header, | ||
alg: issuer.alg || options.header?.alg || JWT_ALG | ||
})); | ||
} else { | ||
const did = issuer[0].did; | ||
const issuers = []; | ||
for (const iss of issuer) { | ||
if (iss.did !== did) { | ||
throw new Error('All issuers must be the same did to comply with the Verifiable Conditions spec'); | ||
} | ||
issuers.push({ | ||
issuer: iss.did || parsedPayload.iss || '', | ||
signer: iss.signer, | ||
alg: iss.alg || options.header?.alg || JWT_ALG | ||
}); | ||
} | ||
return Promise.resolve(createMultisignatureJWT(parsedPayload, { | ||
...options | ||
}, issuers)); | ||
} | ||
} catch (e) { | ||
@@ -905,4 +821,4 @@ return Promise.reject(e); | ||
validateContext(payload.vp['@context']); | ||
validateVpType(payload.vp.type); // empty credential array is allowed | ||
validateVpType(payload.vp.type); | ||
// empty credential array is allowed | ||
if (payload.vp.verifiableCredential && payload.vp.verifiableCredential.length >= 1) { | ||
@@ -917,3 +833,2 @@ for (const vc of asArray(payload.vp.verifiableCredential)) { | ||
} | ||
if (payload.exp) validateTimestamp(payload.exp); | ||
@@ -923,4 +838,4 @@ } | ||
validateContext(payload['@context']); | ||
validateVpType(payload.type); // empty credential array is allowed | ||
validateVpType(payload.type); | ||
// empty credential array is allowed | ||
if (payload.verifiableCredential && payload.verifiableCredential.length >= 1) { | ||
@@ -935,3 +850,2 @@ for (const vc of payload.verifiableCredential) { | ||
} | ||
if (payload.expirationDate) validateTimestamp(payload.expirationDate); | ||
@@ -943,7 +857,5 @@ } | ||
} | ||
if (options.domain) { | ||
// aud might be an array | ||
let matchedAudience; | ||
if (payload.aud) { | ||
@@ -953,3 +865,2 @@ const audArray = Array.isArray(payload.aud) ? payload.aud : [payload.aud]; | ||
} | ||
if (typeof matchedAudience === 'undefined') { | ||
@@ -956,0 +867,0 @@ throw new Error(`${VC_ERROR.AUTH_ERROR}: Presentation does not contain the mandatory domain (JWT: aud) for : ${options.domain}`); |
@@ -19,9 +19,7 @@ (function (global, factory) { | ||
*/ | ||
const additionalPropNames = ['evidence', 'termsOfUse', 'refreshService', 'credentialSchema', 'credentialStatus']; // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const additionalPropNames = ['evidence', 'termsOfUse', 'refreshService', 'credentialSchema', 'credentialStatus']; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function asArray(arg) { | ||
return Array.isArray(arg) ? arg : [arg]; | ||
} | ||
function deepCopy(source) { | ||
@@ -34,7 +32,5 @@ return Array.isArray(source) ? source.map(item => deepCopy(item)) : source instanceof Date ? new Date(source.getTime()) : source && typeof source === 'object' ? Object.getOwnPropertyNames(source).reduce((o, prop) => { | ||
} | ||
function notEmpty(value) { | ||
return value !== null && value !== undefined; | ||
} | ||
function cleanUndefined(input) { | ||
@@ -44,15 +40,14 @@ if (typeof input !== 'object') { | ||
} | ||
const obj = { ...input | ||
const obj = { | ||
...input | ||
}; | ||
Object.keys(obj).forEach(key => obj[key] === undefined && delete obj[key]); | ||
return obj; | ||
} // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function isLegacyAttestationFormat(payload) { | ||
// payload is an object and has all the required fields of old attestation format | ||
return typeof payload === 'object' && payload.sub && payload.iss && payload.claim && payload.iat; | ||
} // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function attestationToVcFormat(payload) { | ||
@@ -66,3 +61,4 @@ const { | ||
} = payload; | ||
const result = { ...rest, | ||
const result = { | ||
...rest, | ||
nbf: nbf ? nbf : iat, | ||
@@ -78,18 +74,14 @@ vc: { | ||
} | ||
function normalizeJwtCredentialPayload(input, removeOriginalFields = true) { | ||
let result = deepCopy(input); | ||
if (isLegacyAttestationFormat(input)) { | ||
result = attestationToVcFormat(input); | ||
} // FIXME: handle case when credentialSubject(s) are not object types | ||
result.credentialSubject = { ...input.credentialSubject, | ||
} | ||
// FIXME: handle case when credentialSubject(s) are not object types | ||
result.credentialSubject = { | ||
...input.credentialSubject, | ||
...input.vc?.credentialSubject | ||
}; | ||
if (input.sub && !input.credentialSubject?.id && result.credentialSubject) { | ||
result.credentialSubject.id = input.sub; | ||
if (removeOriginalFields) { | ||
@@ -99,7 +91,5 @@ delete result.sub; | ||
} | ||
if (removeOriginalFields) { | ||
delete result.vc?.credentialSubject; | ||
} | ||
if (typeof input.issuer === 'undefined' || typeof input.issuer === 'object') { | ||
@@ -110,3 +100,2 @@ result.issuer = cleanUndefined({ | ||
}); | ||
if (removeOriginalFields && !input.issuer?.id) { | ||
@@ -116,6 +105,4 @@ delete result.iss; | ||
} | ||
if (!input.id && input.jti) { | ||
result.id = result.id || result.jti; | ||
if (removeOriginalFields) { | ||
@@ -125,10 +112,7 @@ delete result.jti; | ||
} | ||
const types = [...asArray(result.type), ...asArray(result.vc?.type)].filter(notEmpty); | ||
result.type = [...new Set(types)]; | ||
if (removeOriginalFields) { | ||
delete result.vc?.type; | ||
} | ||
for (const prop of additionalPropNames) { | ||
@@ -139,3 +123,2 @@ if (input.vc && input.vc[prop]) { | ||
} | ||
if (removeOriginalFields) { | ||
@@ -146,6 +129,4 @@ delete result.vc[prop]; | ||
} | ||
const contextArray = [...asArray(input.context), ...asArray(input['@context']), ...asArray(input.vc?.['@context'])].filter(notEmpty); | ||
result['@context'] = [...new Set(contextArray)]; | ||
if (removeOriginalFields) { | ||
@@ -155,6 +136,4 @@ delete result.context; | ||
} | ||
if (!input.issuanceDate && (input.iat || input.nbf)) { | ||
result.issuanceDate = new Date((input.nbf || input.iat) * 1000).toISOString(); | ||
if (removeOriginalFields) { | ||
@@ -168,6 +147,4 @@ if (input.nbf) { | ||
} | ||
if (!input.expirationDate && input.exp) { | ||
result.expirationDate = new Date(input.exp * 1000).toISOString(); | ||
if (removeOriginalFields) { | ||
@@ -177,3 +154,2 @@ delete result.exp; | ||
} | ||
if (removeOriginalFields) { | ||
@@ -183,11 +159,8 @@ if (result.vc && Object.keys(result.vc).length === 0) { | ||
} | ||
} // FIXME: interpret `aud` property as `verifier` | ||
} | ||
// FIXME: interpret `aud` property as `verifier` | ||
return result; | ||
} | ||
function normalizeJwtCredential(input, removeOriginalFields = true) { | ||
let decoded; | ||
try { | ||
@@ -198,4 +171,4 @@ decoded = didJwt.decodeJWT(input); | ||
} | ||
return { ...normalizeJwtCredentialPayload(decoded.payload, removeOriginalFields), | ||
return { | ||
...normalizeJwtCredentialPayload(decoded.payload, removeOriginalFields), | ||
proof: { | ||
@@ -213,4 +186,2 @@ type: DEFAULT_JWT_PROOF_TYPE, | ||
*/ | ||
function normalizeCredential(input, removeOriginalFields = true) { | ||
@@ -222,3 +193,2 @@ if (typeof input === 'string') { | ||
let parsed; | ||
try { | ||
@@ -229,3 +199,2 @@ parsed = JSON.parse(input); | ||
} | ||
return normalizeCredential(parsed, removeOriginalFields); | ||
@@ -235,3 +204,4 @@ } | ||
// TODO: test that it correctly propagates app specific proof properties | ||
return deepCopy({ ...normalizeJwtCredential(input.proof.jwt, removeOriginalFields), | ||
return deepCopy({ | ||
...normalizeJwtCredential(input.proof.jwt, removeOriginalFields), | ||
proof: input.proof | ||
@@ -254,7 +224,7 @@ }); | ||
*/ | ||
function transformCredentialInput(input, removeOriginalFields = true) { | ||
if (Array.isArray(input.credentialSubject)) throw Error('credentialSubject of type array not supported'); | ||
const result = deepCopy({ | ||
vc: { ...input.vc | ||
vc: { | ||
...input.vc | ||
}, | ||
@@ -264,9 +234,8 @@ ...input | ||
result.vc = result.vc; | ||
const credentialSubject = { ...input.credentialSubject, | ||
const credentialSubject = { | ||
...input.credentialSubject, | ||
...input.vc?.credentialSubject | ||
}; | ||
if (!input.sub) { | ||
result.sub = input.credentialSubject?.id; | ||
if (removeOriginalFields) { | ||
@@ -276,6 +245,4 @@ delete credentialSubject.id; | ||
} | ||
const contextEntries = [...asArray(input.context), ...asArray(input['@context']), ...asArray(input.vc?.['@context'])].filter(notEmpty); | ||
result.vc['@context'] = [...new Set(contextEntries)]; | ||
if (removeOriginalFields) { | ||
@@ -285,13 +252,9 @@ delete result.context; | ||
} | ||
const types = [...asArray(input.type), ...asArray(input.vc?.type)].filter(notEmpty); | ||
result.vc.type = [...new Set(types)]; | ||
if (removeOriginalFields) { | ||
delete result.type; | ||
} | ||
if (input.id && Object.getOwnPropertyNames(input).indexOf('jti') === -1) { | ||
result.jti = input.id; | ||
if (removeOriginalFields) { | ||
@@ -301,9 +264,6 @@ delete result.id; | ||
} | ||
if (input.issuanceDate && Object.getOwnPropertyNames(input).indexOf('nbf') === -1) { | ||
const converted = Date.parse(input.issuanceDate); | ||
if (!isNaN(converted)) { | ||
result.nbf = Math.floor(converted / 1000); | ||
if (removeOriginalFields) { | ||
@@ -314,9 +274,6 @@ delete result.issuanceDate; | ||
} | ||
if (input.expirationDate && Object.getOwnPropertyNames(input).indexOf('exp') === -1) { | ||
const converted = Date.parse(input.expirationDate); | ||
if (!isNaN(converted)) { | ||
result.exp = Math.floor(converted / 1000); | ||
if (removeOriginalFields) { | ||
@@ -327,10 +284,7 @@ delete result.expirationDate; | ||
} | ||
if (input.issuer && Object.getOwnPropertyNames(input).indexOf('iss') === -1) { | ||
if (typeof input.issuer === 'object') { | ||
result.iss = input.issuer?.id; | ||
if (removeOriginalFields) { | ||
delete result.issuer.id; | ||
if (Object.keys(result.issuer).length === 0) { | ||
@@ -342,3 +296,2 @@ delete result.issuer; | ||
result.iss = input.iss || '' + input.issuer; | ||
if (removeOriginalFields) { | ||
@@ -349,9 +302,6 @@ delete result.issuer; | ||
} | ||
result.vc.credentialSubject = credentialSubject; | ||
if (removeOriginalFields) { | ||
delete result.credentialSubject; | ||
} | ||
for (const prop of additionalPropNames) { | ||
@@ -362,3 +312,2 @@ if (input[prop]) { | ||
} | ||
if (removeOriginalFields) { | ||
@@ -369,6 +318,4 @@ delete result[prop]; | ||
} | ||
return result; | ||
} | ||
function normalizeJwtPresentationPayload(input, removeOriginalFields = true) { | ||
@@ -380,10 +327,7 @@ const result = deepCopy(input); | ||
}); | ||
if (removeOriginalFields) { | ||
delete result.vp?.verifiableCredential; | ||
} | ||
if (input.iss && !input.holder) { | ||
result.holder = input.iss; | ||
if (removeOriginalFields) { | ||
@@ -393,7 +337,5 @@ delete result.iss; | ||
} | ||
if (input.aud) { | ||
result.verifier = [...asArray(input.verifier), ...asArray(input.aud)].filter(notEmpty); | ||
result.verifier = [...new Set(result.verifier)]; | ||
if (removeOriginalFields) { | ||
@@ -403,6 +345,4 @@ delete result.aud; | ||
} | ||
if (input.jti && Object.getOwnPropertyNames(input).indexOf('id') === -1) { | ||
result.id = input.id || input.jti; | ||
if (removeOriginalFields) { | ||
@@ -412,13 +352,9 @@ delete result.jti; | ||
} | ||
const types = [...asArray(input.type), ...asArray(input.vp?.type)].filter(notEmpty); | ||
result.type = [...new Set(types)]; | ||
if (removeOriginalFields) { | ||
delete result.vp?.type; | ||
} | ||
const contexts = [...asArray(input.context), ...asArray(input['@context']), ...asArray(input.vp?.['@context'])].filter(notEmpty); | ||
result['@context'] = [...new Set(contexts)]; | ||
if (removeOriginalFields) { | ||
@@ -428,6 +364,4 @@ delete result.context; | ||
} | ||
if (!input.issuanceDate && (input.iat || input.nbf)) { | ||
result.issuanceDate = new Date((input.nbf || input.iat) * 1000).toISOString(); | ||
if (removeOriginalFields) { | ||
@@ -441,6 +375,4 @@ if (input.nbf) { | ||
} | ||
if (!input.expirationDate && input.exp) { | ||
result.expirationDate = new Date(input.exp * 1000).toISOString(); | ||
if (removeOriginalFields) { | ||
@@ -450,3 +382,2 @@ delete result.exp; | ||
} | ||
if (result.vp && Object.keys(result.vp).length === 0) { | ||
@@ -457,9 +388,6 @@ if (removeOriginalFields) { | ||
} | ||
return result; | ||
} | ||
function normalizeJwtPresentation(input, removeOriginalFields = true) { | ||
let decoded; | ||
try { | ||
@@ -470,4 +398,4 @@ decoded = didJwt.decodeJWT(input); | ||
} | ||
return { ...normalizeJwtPresentationPayload(decoded.payload, removeOriginalFields), | ||
return { | ||
...normalizeJwtPresentationPayload(decoded.payload, removeOriginalFields), | ||
proof: { | ||
@@ -483,4 +411,2 @@ type: DEFAULT_JWT_PROOF_TYPE, | ||
*/ | ||
function normalizePresentation(input, removeOriginalFields = true) { | ||
@@ -492,3 +418,2 @@ if (typeof input === 'string') { | ||
let parsed; | ||
try { | ||
@@ -499,3 +424,2 @@ parsed = JSON.parse(input); | ||
} | ||
return normalizePresentation(parsed, removeOriginalFields); | ||
@@ -505,3 +429,4 @@ } | ||
// TODO: test that it correctly propagates app specific proof properties | ||
return { ...normalizeJwtPresentation(input.proof.jwt, removeOriginalFields), | ||
return { | ||
...normalizeJwtPresentation(input.proof.jwt, removeOriginalFields), | ||
proof: input.proof | ||
@@ -524,6 +449,6 @@ }; | ||
*/ | ||
function transformPresentationInput(input, removeOriginalFields = true) { | ||
const result = deepCopy({ | ||
vp: { ...input.vp | ||
vp: { | ||
...input.vp | ||
}, | ||
@@ -535,3 +460,2 @@ ...input | ||
result.vp['@context'] = [...new Set(contextEntries)]; | ||
if (removeOriginalFields) { | ||
@@ -541,13 +465,9 @@ delete result.context; | ||
} | ||
const types = [...asArray(input.type), ...asArray(input.vp?.type)].filter(notEmpty); | ||
result.vp.type = [...new Set(types)]; | ||
if (removeOriginalFields) { | ||
delete result.type; | ||
} | ||
if (input.id && Object.getOwnPropertyNames(input).indexOf('jti') === -1) { | ||
result.jti = input.id; | ||
if (removeOriginalFields) { | ||
@@ -557,9 +477,6 @@ delete result.id; | ||
} | ||
if (input.issuanceDate && Object.getOwnPropertyNames(input).indexOf('nbf') === -1) { | ||
const converted = Date.parse(input.issuanceDate); | ||
if (!isNaN(converted)) { | ||
result.nbf = Math.floor(converted / 1000); | ||
if (removeOriginalFields) { | ||
@@ -570,9 +487,6 @@ delete result.issuanceDate; | ||
} | ||
if (input.expirationDate && Object.getOwnPropertyNames(input).indexOf('exp') === -1) { | ||
const converted = Date.parse(input.expirationDate); | ||
if (!isNaN(converted)) { | ||
result.exp = Math.floor(converted / 1000); | ||
if (removeOriginalFields) { | ||
@@ -583,3 +497,2 @@ delete result.expirationDate; | ||
} | ||
if (result.verifiableCredential || result.vp?.verifiableCredential) { | ||
@@ -594,11 +507,8 @@ result.vp.verifiableCredential = [...asArray(result.verifiableCredential), ...asArray(result.vp?.verifiableCredential)].filter(notEmpty).map(credential => { | ||
} | ||
if (removeOriginalFields) { | ||
delete result.verifiableCredential; | ||
} | ||
if (input.holder && Object.getOwnPropertyNames(input).indexOf('iss') === -1) { | ||
if (typeof input.holder === 'string') { | ||
result.iss = input.holder; | ||
if (removeOriginalFields) { | ||
@@ -609,7 +519,5 @@ delete result.holder; | ||
} | ||
if (input.verifier) { | ||
const audience = [...asArray(input.verifier), ...asArray(input.aud)].filter(notEmpty); | ||
result.aud = [...new Set(audience)]; | ||
if (removeOriginalFields) { | ||
@@ -619,3 +527,2 @@ delete result.verifier; | ||
} | ||
return result; | ||
@@ -628,3 +535,2 @@ } | ||
*/ | ||
const VC_ERROR = { | ||
@@ -636,3 +542,2 @@ /** | ||
SCHEMA_ERROR: 'schema_error', | ||
/** | ||
@@ -642,3 +547,2 @@ * Thrown when the input is not a JWT string | ||
FORMAT_ERROR: 'format_error', | ||
/** | ||
@@ -652,11 +556,10 @@ * Thrown when verifying a presentation where `challenge` and/or `domain` don't match the expected values. | ||
*/ | ||
const VC_JWT_ERROR = { ...VC_ERROR, | ||
const VC_JWT_ERROR = { | ||
...VC_ERROR, | ||
...didJwt.JWT_ERROR | ||
}; // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
}; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function isDateObject(input) { | ||
return input && !isNaN(input) && Object.prototype.toString.call(input) === '[object Date]'; | ||
} | ||
function validateJwtFormat(value) { | ||
@@ -666,3 +569,4 @@ if (typeof value === 'string' && !value.match(JWT_FORMAT)) { | ||
} | ||
} // The main scenario we want to guard against is having a timestamp in milliseconds | ||
} | ||
// The main scenario we want to guard against is having a timestamp in milliseconds | ||
// instead of seconds (ex: from new Date().getTime()). | ||
@@ -674,3 +578,2 @@ // We will check the number of digits and assume that any number with 12 or more | ||
// 12 digits max is 999999999999 -> 09/27/33658 @ 1:46am (UTC) | ||
function validateTimestamp(value) { | ||
@@ -689,3 +592,2 @@ if (typeof value === 'number') { | ||
const input = asArray(value); | ||
if (input.length < 1 || input.indexOf(DEFAULT_CONTEXT) === -1) { | ||
@@ -697,3 +599,2 @@ throw new TypeError(`${VC_ERROR.SCHEMA_ERROR}: @context is missing default context "${DEFAULT_CONTEXT}"`); | ||
const input = asArray(value); | ||
if (input.length < 1 || input.indexOf(DEFAULT_VC_TYPE) === -1) { | ||
@@ -705,3 +606,2 @@ throw new TypeError(`${VC_ERROR.SCHEMA_ERROR}: type is missing default "${DEFAULT_VC_TYPE}"`); | ||
const input = asArray(value); | ||
if (input.length < 1 || input.indexOf(DEFAULT_VP_TYPE) === -1) { | ||
@@ -734,3 +634,4 @@ throw new TypeError(`${VC_ERROR.SCHEMA_ERROR}: type is missing default "${DEFAULT_VP_TYPE}"`); | ||
...options, | ||
policies: { ...options?.policies, | ||
policies: { | ||
...options?.policies, | ||
nbf, | ||
@@ -747,7 +648,5 @@ exp, | ||
verified.verifiablePresentation = normalizePresentation(verified.jwt, options?.removeOriginalFields); | ||
if (options?.policies?.format !== false) { | ||
validatePresentationPayload(verified.verifiablePresentation); | ||
} | ||
return verified; | ||
@@ -759,3 +658,2 @@ }); | ||
}; | ||
/** | ||
@@ -775,4 +673,6 @@ * Verifies and validates a VerifiableCredential that is encoded as a JWT according to the W3C spec. | ||
const exp = options?.policies?.expirationDate === false ? false : undefined; | ||
options = { ...options, | ||
policies: { ...options?.policies, | ||
options = { | ||
...options, | ||
policies: { | ||
...options?.policies, | ||
nbf, | ||
@@ -788,7 +688,5 @@ exp, | ||
verified.verifiableCredential = normalizeCredential(verified.jwt, options?.removeOriginalFields); | ||
if (options?.policies?.format !== false) { | ||
validateCredentialPayload(verified.verifiableCredential); | ||
} | ||
return verified; | ||
@@ -807,3 +705,2 @@ }); | ||
*/ | ||
/** | ||
@@ -831,9 +728,8 @@ * Creates a VerifiablePresentation JWT given a `PresentationPayload` or `JwtPresentationPayload` and an `Issuer`. | ||
...transformPresentationInput(payload, options?.removeOriginalFields) | ||
}; // add challenge to nonce | ||
}; | ||
// add challenge to nonce | ||
if (options.challenge && Object.getOwnPropertyNames(parsedPayload).indexOf('nonce') === -1) { | ||
parsedPayload.nonce = options.challenge; | ||
} // add domain to audience. | ||
} | ||
// add domain to audience. | ||
if (options.domain) { | ||
@@ -843,8 +739,9 @@ const audience = [...asArray(options.domain), ...asArray(parsedPayload.aud)].filter(notEmpty); | ||
} | ||
validateJwtPresentationPayload(parsedPayload); | ||
return Promise.resolve(didJwt.createJWT(parsedPayload, { ...options, | ||
return Promise.resolve(didJwt.createJWT(parsedPayload, { | ||
...options, | ||
issuer: holder.did || parsedPayload.iss || '', | ||
signer: holder.signer | ||
}, { ...options.header, | ||
}, { | ||
...options.header, | ||
alg: holder.alg || options.header?.alg || JWT_ALG | ||
@@ -856,3 +753,2 @@ })); | ||
}; | ||
/** | ||
@@ -881,8 +777,28 @@ * Creates a VerifiableCredential given a `CredentialPayload` or `JwtCredentialPayload` and an `Issuer`. | ||
validateJwtCredentialPayload(parsedPayload); | ||
return Promise.resolve(didJwt.createJWT(parsedPayload, { ...options, | ||
issuer: issuer.did || parsedPayload.iss || '', | ||
signer: issuer.signer | ||
}, { ...options.header, | ||
alg: issuer.alg || options.header?.alg || JWT_ALG | ||
})); | ||
if (!Array.isArray(issuer)) { | ||
return Promise.resolve(didJwt.createJWT(parsedPayload, { | ||
...options, | ||
issuer: issuer.did || parsedPayload.iss || '', | ||
signer: issuer.signer | ||
}, { | ||
...options.header, | ||
alg: issuer.alg || options.header?.alg || JWT_ALG | ||
})); | ||
} else { | ||
const did = issuer[0].did; | ||
const issuers = []; | ||
for (const iss of issuer) { | ||
if (iss.did !== did) { | ||
throw new Error('All issuers must be the same did to comply with the Verifiable Conditions spec'); | ||
} | ||
issuers.push({ | ||
issuer: iss.did || parsedPayload.iss || '', | ||
signer: iss.signer, | ||
alg: iss.alg || options.header?.alg || JWT_ALG | ||
}); | ||
} | ||
return Promise.resolve(didJwt.createMultisignatureJWT(parsedPayload, { | ||
...options | ||
}, issuers)); | ||
} | ||
} catch (e) { | ||
@@ -908,4 +824,4 @@ return Promise.reject(e); | ||
validateContext(payload.vp['@context']); | ||
validateVpType(payload.vp.type); // empty credential array is allowed | ||
validateVpType(payload.vp.type); | ||
// empty credential array is allowed | ||
if (payload.vp.verifiableCredential && payload.vp.verifiableCredential.length >= 1) { | ||
@@ -920,3 +836,2 @@ for (const vc of asArray(payload.vp.verifiableCredential)) { | ||
} | ||
if (payload.exp) validateTimestamp(payload.exp); | ||
@@ -926,4 +841,4 @@ } | ||
validateContext(payload['@context']); | ||
validateVpType(payload.type); // empty credential array is allowed | ||
validateVpType(payload.type); | ||
// empty credential array is allowed | ||
if (payload.verifiableCredential && payload.verifiableCredential.length >= 1) { | ||
@@ -938,3 +853,2 @@ for (const vc of payload.verifiableCredential) { | ||
} | ||
if (payload.expirationDate) validateTimestamp(payload.expirationDate); | ||
@@ -946,7 +860,5 @@ } | ||
} | ||
if (options.domain) { | ||
// aud might be an array | ||
let matchedAudience; | ||
if (payload.aud) { | ||
@@ -956,3 +868,2 @@ const audArray = Array.isArray(payload.aud) ? payload.aud : [payload.aud]; | ||
} | ||
if (typeof matchedAudience === 'undefined') { | ||
@@ -959,0 +870,0 @@ throw new Error(`${VC_ERROR.AUTH_ERROR}: Presentation does not contain the mandatory domain (JWT: aud) for : ${options.domain}`); |
{ | ||
"name": "did-jwt-vc", | ||
"version": "3.1.4", | ||
"version": "3.2.0", | ||
"description": "Create and verify W3C Verifiable Credentials and Presentations in JWT format", | ||
@@ -39,4 +39,4 @@ "type": "module", | ||
"dependencies": { | ||
"did-jwt": "^7.0.0", | ||
"did-resolver": "^4.0.0" | ||
"did-jwt": "^7.1.0", | ||
"did-resolver": "^4.1.0" | ||
}, | ||
@@ -43,0 +43,0 @@ "repository": { |
@@ -1,2 +0,2 @@ | ||
import { createJWT, verifyJWT } from 'did-jwt' | ||
import { createJWT, createMultisignatureJWT, verifyJWT } from 'did-jwt' | ||
import { Resolvable } from 'did-resolver' | ||
@@ -82,3 +82,3 @@ import * as validators from './validators' | ||
payload: JwtCredentialPayload | CredentialPayload, | ||
issuer: Issuer, | ||
issuer: Issuer | Issuer[], | ||
options: CreateCredentialOptions = {} | ||
@@ -91,14 +91,32 @@ ): Promise<JWT> { | ||
validateJwtCredentialPayload(parsedPayload) | ||
return createJWT( | ||
parsedPayload, | ||
{ | ||
...options, | ||
issuer: issuer.did || parsedPayload.iss || '', | ||
signer: issuer.signer, | ||
}, | ||
{ | ||
...options.header, | ||
alg: issuer.alg || options.header?.alg || JWT_ALG, | ||
if (!Array.isArray(issuer)) { | ||
return createJWT( | ||
parsedPayload, | ||
{ | ||
...options, | ||
issuer: issuer.did || parsedPayload.iss || '', | ||
signer: issuer.signer, | ||
}, | ||
{ | ||
...options.header, | ||
alg: issuer.alg || options.header?.alg || JWT_ALG, | ||
} | ||
) | ||
} else { | ||
const did = issuer[0].did | ||
const issuers = [] | ||
for (const iss of issuer) { | ||
if (iss.did !== did) { | ||
throw new Error('All issuers must be the same did to comply with the Verifiable Conditions spec') | ||
} | ||
issuers.push({ | ||
issuer: iss.did || parsedPayload.iss || '', | ||
signer: iss.signer, | ||
alg: iss.alg || options.header?.alg || JWT_ALG, | ||
}) | ||
} | ||
) | ||
return createMultisignatureJWT(parsedPayload, { ...options }, issuers) | ||
} | ||
} | ||
@@ -105,0 +123,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
617616
6855
Updateddid-jwt@^7.1.0
Updateddid-resolver@^4.1.0