@credo-ts/core
Version:
<p align="center"> <br /> <img alt="Credo Logo" src="https://github.com/openwallet-foundation/credo-ts/blob/c7886cb8377ceb8ee4efe8d264211e561a75072d/images/credo-logo.png" height="250px" /> </p> <h1 align="center"><b>Credo - Core</b></h1
1 lines • 14.3 kB
Source Map (JSON)
{"version":3,"file":"credentialUse.mjs","names":[],"sources":["../../src/utils/credentialUse.ts"],"sourcesContent":["import { AgentContext } from '../agent'\nimport { CredoError } from '../error/CredoError'\nimport { Mdoc } from '../modules/mdoc/Mdoc'\nimport { MdocRecord, type MdocRecordInstances, MdocRepository } from '../modules/mdoc/repository'\nimport { decodeSdJwtVc } from '../modules/sd-jwt-vc/decodeSdJwtVc'\nimport { SdJwtVcRecord, type SdJwtVcRecordInstances, SdJwtVcRepository } from '../modules/sd-jwt-vc/repository'\nimport type { SdJwtVc } from '../modules/sd-jwt-vc/SdJwtVcService'\nimport {\n W3cJsonLdVerifiableCredential,\n W3cJwtVerifiableCredential,\n W3cV2JwtVerifiableCredential,\n W3cV2SdJwtVerifiableCredential,\n type W3cV2VerifiableCredential,\n type W3cVerifiableCredential,\n} from '../modules/vc'\nimport {\n W3cCredentialRecord,\n type W3cCredentialRecordInstances,\n W3cCredentialRepository,\n W3cV2CredentialRecord,\n type W3cV2CredentialRecordInstances,\n W3cV2CredentialRepository,\n} from '../modules/vc/repository'\nimport {\n CredentialMultiInstanceState,\n CredentialMultiInstanceUseMode,\n CredentialMultiInstanceUseUpdateMode,\n} from './credentialUseTypes'\n\nexport { CredentialMultiInstanceUseMode, CredentialMultiInstanceUseUpdateMode, CredentialMultiInstanceState }\n\ntype CredentialRecord = W3cCredentialRecord | SdJwtVcRecord | MdocRecord | W3cV2CredentialRecord\n\ntype CredentialInstanceReturnType<Record extends CredentialRecord> = Record extends W3cCredentialRecord\n ? W3cVerifiableCredential\n : Record extends W3cV2CredentialRecord\n ? W3cV2VerifiableCredential\n : Record extends SdJwtVcRecord\n ? SdJwtVc\n : Record extends MdocRecord\n ? Mdoc\n : CredentialRecord\n\ntype CredentialRepositoryForRecord<Record extends CredentialRecord> = Record extends W3cCredentialRecord\n ? W3cCredentialRepository\n : Record extends W3cV2CredentialRepository\n ? W3cV2VerifiableCredential\n : Record extends SdJwtVcRepository\n ? SdJwtVc\n : Record extends MdocRepository\n ? Mdoc\n : W3cCredentialRepository | W3cV2CredentialRepository | SdJwtVcRepository | MdocRepository\n\n/**\n * Checks whether an instance can be used from the provided record based on\n * the required credential use mode.\n */\nexport function canUseInstanceFromCredentialRecord({\n credentialRecord,\n useMode,\n}: {\n credentialRecord: CredentialRecord\n useMode: CredentialMultiInstanceUseMode\n}) {\n // If we're not required to use a new instance, we can always return the first instance\n if (\n useMode === CredentialMultiInstanceUseMode.First ||\n useMode === CredentialMultiInstanceUseMode.NewOrFirst ||\n (useMode === CredentialMultiInstanceUseMode.NewIfReceivedInBatch &&\n [CredentialMultiInstanceState.SingleInstanceUnused, CredentialMultiInstanceState.SingleInstanceUsed].includes(\n credentialRecord.multiInstanceState\n ))\n ) {\n return true\n }\n\n // Otherwise we return whether we can use a new instance\n return (\n credentialRecord.multiInstanceState === CredentialMultiInstanceState.MultiInstanceFirstUnused ||\n credentialRecord.multiInstanceState === CredentialMultiInstanceState.SingleInstanceUnused\n )\n}\n\nexport interface UseInstanceFromCredentialRecordOptions<Record extends CredentialRecord> {\n agentContext: AgentContext\n\n /**\n * Which mode to use for usage of the credential instance. See {@link CredentialMultiInstanceUseMode} for\n * more information on the available options\n */\n useMode: CredentialMultiInstanceUseMode\n credentialRecord: Record\n\n /**\n * The update mode for the credential record when a new instance is used.\n *\n * @default `CredentialMultiInstanceUseUpdateMode.RefetchAndUpdateWithLock`\n */\n updateMode?: CredentialMultiInstanceUseUpdateMode\n}\n\nexport interface UseInstanceFromCredentialRecordReturn<Record extends CredentialRecord> {\n /**\n * The credential instance with the kms key id\n */\n credentialInstance: CredentialInstanceReturnType<Record>\n\n /**\n * If the first instance was used, this value will be `true`. The first time\n * the first instance is used, technically the credential is not reused yet,\n * but we make no distinction between this.\n */\n isReused: boolean\n\n /**\n * Whether the last new instance was used. If `isReused` is `true` this value will\n * always be `false`.\n *\n * The last new instance is the second entry in the mdoc instances, since the first\n * one is reserved for 'reused' usage.\n */\n isLastNewInstance: boolean\n}\n\n/**\n * Extract an instance with the correct kms key id from the credential instances\n * on the record. Note that if an instance is extracted (that is not reused) it\n * will remove the instance from the record, and the record should be updated in\n * storage after usage.\n *\n * Note that the last credential instance is never removed from the record. So if the\n * method indicates the last instance has been used, you should remove the credential\n * from storage if you don't want it to be used anymore in the future.\n */\nexport async function useInstanceFromCredentialRecord<Record extends CredentialRecord>({\n credentialRecord,\n useMode,\n agentContext,\n updateMode,\n}: UseInstanceFromCredentialRecordOptions<Record>): Promise<UseInstanceFromCredentialRecordReturn<Record>> {\n let extractResult = extractInstanceAndUpdateRecord({\n credentialRecord,\n useMode,\n })\n\n if (\n (!extractResult.isReused || extractResult.isLastNewInstance) &&\n updateMode === CredentialMultiInstanceUseUpdateMode.Update\n ) {\n await updateCredentialRecord(agentContext, credentialRecord)\n }\n\n if (\n (!extractResult.isReused || extractResult.isLastNewInstance) &&\n updateMode === CredentialMultiInstanceUseUpdateMode.RefetchAndUpdateWithLock\n ) {\n // the method is generic, but the types don't work well generically\n const repository = repositoryForRecord(agentContext, credentialRecord) as SdJwtVcRepository\n\n await repository.updateByIdWithLock(\n agentContext,\n credentialRecord.id,\n async (freshCredentialRecord: SdJwtVcRecord) => {\n extractResult = extractInstanceAndUpdateRecord({\n credentialRecord: freshCredentialRecord,\n useMode,\n })\n\n return freshCredentialRecord\n }\n )\n }\n\n let transformedCredentialInstance: W3cVerifiableCredential | Mdoc | SdJwtVc | W3cV2VerifiableCredential\n if (credentialRecord instanceof MdocRecord) {\n const { issuerSignedBase64Url, kmsKeyId } = extractResult.credentialInstance as MdocRecordInstances[0]\n transformedCredentialInstance = Mdoc.fromBase64Url(issuerSignedBase64Url)\n transformedCredentialInstance.deviceKeyId = kmsKeyId ?? transformedCredentialInstance.deviceKey.legacyKeyId\n } else if (credentialRecord instanceof SdJwtVcRecord) {\n const { compactSdJwtVc, kmsKeyId } = extractResult.credentialInstance as SdJwtVcRecordInstances[0]\n\n transformedCredentialInstance = {\n ...decodeSdJwtVc(compactSdJwtVc, credentialRecord.typeMetadata),\n kmsKeyId,\n }\n } else if (credentialRecord instanceof W3cCredentialRecord) {\n const { credential } = extractResult.credentialInstance as W3cCredentialRecordInstances[0]\n\n transformedCredentialInstance =\n typeof credential === 'string'\n ? W3cJwtVerifiableCredential.fromSerializedJwt(credential)\n : W3cJsonLdVerifiableCredential.fromJson(credential)\n } else if (credentialRecord instanceof W3cV2CredentialRecord) {\n const { credential } = extractResult.credentialInstance as W3cV2CredentialRecordInstances[0]\n\n transformedCredentialInstance = credential.includes('~')\n ? W3cV2SdJwtVerifiableCredential.fromCompact(credential)\n : W3cV2JwtVerifiableCredential.fromCompact(credential)\n } else {\n throw new CredoError('Unsupported record type')\n }\n\n return {\n credentialInstance: transformedCredentialInstance as CredentialInstanceReturnType<Record>,\n isReused: extractResult.isReused,\n isLastNewInstance: extractResult.isLastNewInstance,\n }\n}\n\nfunction extractInstanceAndUpdateRecord<Record extends CredentialRecord>({\n credentialRecord,\n useMode,\n}: Pick<UseInstanceFromCredentialRecordOptions<Record>, 'credentialRecord' | 'useMode'>) {\n if (credentialRecord.credentialInstances.length === 1 || useMode === CredentialMultiInstanceUseMode.First) {\n const isFirstUnused =\n credentialRecord.multiInstanceState === CredentialMultiInstanceState.MultiInstanceFirstUnused ||\n credentialRecord.multiInstanceState === CredentialMultiInstanceState.SingleInstanceUnused\n\n const isMultiInstance =\n credentialRecord.multiInstanceState === CredentialMultiInstanceState.MultiInstanceFirstUnused ||\n credentialRecord.multiInstanceState === CredentialMultiInstanceState.MultiInstanceFirstUsed\n\n const allowsReuse =\n // New does not allow reuse\n useMode === CredentialMultiInstanceUseMode.New\n ? false\n : // NewIfReceivedInBatch only allows reuse if the credential was not multi instance\n useMode === CredentialMultiInstanceUseMode.NewIfReceivedInBatch\n ? !isMultiInstance\n : // Otherwise we allow reuse (First or NewOrFirst)\n true\n\n if (isFirstUnused) {\n credentialRecord.multiInstanceState =\n credentialRecord.multiInstanceState === CredentialMultiInstanceState.SingleInstanceUnused\n ? CredentialMultiInstanceState.SingleInstanceUsed\n : CredentialMultiInstanceState.MultiInstanceFirstUsed\n } else if (!allowsReuse) {\n throw new CredoError(\n `Unable to extract new credential instance from ${credentialRecord.type} with id '${credentialRecord.id}', since it only contains a single credential instance but using a new instance is required due to use mode '${useMode}'.`\n )\n }\n\n return {\n isReused: !isFirstUnused,\n credentialInstance: credentialRecord.credentialInstances[0],\n isLastNewInstance: isFirstUnused,\n }\n }\n\n // We have multiple instances, so we pop the last one (never the first one)\n const _credentialInstance = credentialRecord.credentialInstances.pop()\n if (!_credentialInstance) {\n throw new CredoError(\n `Unable to extract credential instance from ${credentialRecord.type} with id '${credentialRecord.id}', since the credential record does not contain any credential instances.`\n )\n }\n\n return {\n credentialInstance: _credentialInstance,\n isReused: false,\n isLastNewInstance: false,\n }\n}\n\nfunction repositoryForRecord<Record extends CredentialRecord>(\n agentContext: AgentContext,\n record: CredentialRecord\n): CredentialRepositoryForRecord<Record> {\n if (record instanceof W3cCredentialRecord)\n return agentContext.resolve(W3cCredentialRepository) as CredentialRepositoryForRecord<Record>\n if (record instanceof W3cV2CredentialRecord)\n return agentContext.resolve(W3cV2CredentialRepository) as CredentialRepositoryForRecord<Record>\n if (record instanceof MdocRecord) return agentContext.resolve(MdocRepository) as CredentialRepositoryForRecord<Record>\n return agentContext.resolve(SdJwtVcRepository) as CredentialRepositoryForRecord<Record>\n}\n\nfunction updateCredentialRecord(agentContext: AgentContext, record: CredentialRecord) {\n if (record instanceof W3cCredentialRecord)\n return agentContext.resolve(W3cCredentialRepository).update(agentContext, record)\n if (record instanceof W3cV2CredentialRecord)\n return agentContext.resolve(W3cV2CredentialRepository).update(agentContext, record)\n if (record instanceof MdocRecord) return agentContext.resolve(MdocRepository).update(agentContext, record)\n return agentContext.resolve(SdJwtVcRepository).update(agentContext, record)\n}\n"],"mappings":"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"}